C++并发与多线程(锁的使用)

创建和等待多个线程

基础示例

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>

using  namespace std;



void myprint(int num ) 
{

	cout << "func = " << num << " threadid = " << this_thread::get_id() << endl;
	std::this_thread::sleep_for(std::chrono::milliseconds(1));
	
	return;
}



int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl;
	vector<thread>mythreads;
	//创建十个线程,执行函数
	for (int i = 0; i < 10; i++)
	{
#if 0
		thread t(myprint,i);
		mythreads.push_back(t);
#else		
		mythreads.push_back(thread(myprint, i)); //不能用以上写法,因为以上是一个临时对象,结束之后会销毁。  vector内容在push时,会调用拷贝构造函数,如果是一个临时变量会调用移动构造
#endif
	}

	//for(auto it: mythreads)  //这样好像不行,求评论区指出正确写法。
	//{
	//	it.join();
	//}

	for (int i = 0; i < 10; i++)
	{
		mythreads[i].join();
	}
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl;


	return 0;
}



数据共享保护案例代码

产生竞争的代码示例

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <list>
using  namespace std;




class  TA
{
public:
	int m_i; //mutable可以修改变量的值
	list<int> m_list;


	 TA(int i) :m_i(i)   // 列表初始化来初始化对象。  explicit可以禁止隐式转换
	{
		cout << "TA()构造函数被执行" << this<< " threadid = "<< this_thread::get_id() << endl;
	}
	TA(const TA& ta) :m_i(ta.m_i)
	{
		cout << "TA拷贝构造函数" <<this << " threadid = " << this_thread::get_id() << endl;
	}
	TA(const TA&& ta) :m_i(ta.m_i)
	{
		//m_i = 0;
		//ta.m_i = -100;
		cout << "TA移动构造函数" << this << " threadid = " << this_thread::get_id() << endl;
	}
	void thread_work()
	{
		//把收到的消息放到队列线程里。

		cout  << "TA thread_work "  << this << " threadid = " << this_thread::get_id() << endl;
		for (int i = 0; i < 100000;i++)
		{
			m_list.push_back(i); 
			cout << "thread_work执行,插入一个元素" << i << endl;
		}
	}
	void therad_out()
	{
		cout << "TA therad_out " << this << " threadid = " << this_thread::get_id() << endl;

		for (int i = 0; i < 100000; i++)
		{
			if (!m_list.empty())
			{
				int command = m_list.front();
				m_list.pop_front();
				cout << "threadout执行,拿到的元素是" << command << endl;
			}
			else
				cout << "threadout 执行,但是消息队列是空的"<< i << endl;
		}

	}
	~TA()
	{
		cout << "TA析构函数" << this << " threadid = " << this_thread::get_id() <<  endl;
	}
};






int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl<<endl;
	TA ta(10);
	thread myoutobj(&TA::therad_out, ref(ta) ); //第二个参数使用引用可以保证使用同一个对象。
	thread myworkbj(&TA::thread_work, &ta); //第二个参数使用引用可以保证使用同一个对象。

	myoutobj.join();
	myworkbj.join();

	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl;
	


	return 0;
}



使用互斥量解决竞争(mutex)

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <list>
using  namespace std;



class  TA
{
public:
	int m_i; //mutable可以修改变量的值
	list<int> m_list;
	mutex m_mutex;


	TA(int i) :m_i(i)   // 列表初始化来初始化对象。  explicit可以禁止隐式转换
	{
		cout << "TA()构造函数被执行" << this << " threadid = " << this_thread::get_id() << endl;
	}
	TA(const TA& ta) :m_i(ta.m_i)
	{
		cout << "TA拷贝构造函数" << this << " threadid = " << this_thread::get_id() << endl;
	}
	TA(const TA&& ta) :m_i(ta.m_i)
	{
		//m_i = 0;
		//ta.m_i = -100;
		cout << "TA移动构造函数" << this << " threadid = " << this_thread::get_id() << endl;
	}
	void thread_work()
	{
		//把收到的消息放到队列线程里。

		cout << "TA thread_work " << this << " threadid = " << this_thread::get_id() << endl;
		for (int i = 0; i < 100000; i++)
		{
			m_mutex.lock();
			
			m_list.push_back(i);
			cout << "thread_work执行,插入一个元素" << i << endl;
			m_mutex.unlock();
		}
	}
	void therad_out()
	{
		cout << "TA therad_out " << this << " threadid = " << this_thread::get_id() << endl;

		int command = 0;
		for (int i = 0; i < 100000; i++)
		{
			bool ret = outMsgProc(command);
			if (ret == true)
			{
				cout << "threadout执行,拿到的元素是" << command << endl;

			}
			else
			{
				cout << "threadout 执行,但是消息队列是空的" << i << endl;
			}


		}
	}
	bool outMsgProc(int& command)
	{
		m_mutex.lock();
		if (!m_list.empty())
		{
			int command = m_list.front();
			m_list.pop_front();
			m_mutex.unlock();
			return true;
		}
		m_mutex.unlock();
		return false;
	}

	~TA()
	{
		cout << "TA析构函数" << this << " threadid = " << this_thread::get_id() << endl;
	}
};






int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	TA ta(10);
	thread myoutobj(&TA::therad_out, ref(ta)); //第二个参数使用引用可以保证使用同一个对象。
	thread myworkbj(&TA::thread_work, &ta); //第二个参数使用引用可以保证使用同一个对象。

	myoutobj.join();
	myworkbj.join();

	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl;

	

	return 0;
}



智能锁(lock_guard<mutex> )

bool outMsgProc(int& command)
	{
		std::lock_guard<mutex> guard(m_mutex); //智能锁,出去作用域会自动释放,使用之后就不能用lock和unlock了。
		
		//m_mutex.lock();
		if (!m_list.empty())
		{
			int command = m_list.front();
			m_list.pop_front();
			//m_mutex.unlock();
			return true;
		}
		//m_mutex.unlock();
		return false;
	}

死锁问题

死锁是并发编程中的一个经典问题,发生在两个或多个线程在等待对方释放资源时永远阻塞的情况。死锁会导致程序无法继续执行,因为涉及的线程都无法继续向前推进。死锁通常由以下四个必要条件同时出现引起:

  1. 互斥条件:资源不能被共享,只能由一个线程占用。

  2. 占有并等待:线程已经持有至少一个资源,并且等待获取其他线程持有的资源。

  3. 不可剥夺:资源不能被强制从线程中剥夺,只能由线程自己释放。

  4. 循环等待:存在一个线程循环链,链中的每个线程都在等待下一个线程所持有的资源。

死锁的预防和解决策略

  1. 资源有序分配:为资源分配一个全局顺序,所有线程都必须按照这个顺序来请求资源。

  2. 避免占有并等待:线程在请求资源前不持有任何资源,或者一次性请求所有需要的资源。

  3. 资源剥夺:允许系统在必要时从线程中剥夺资源。

  4. 超时机制:线程在请求资源时设置超时,如果在超时时间内没有获取到资源,就释放已持有的资源并重试。

  5. 死锁检测与恢复:系统定期检测死锁,并采取恢复措施,如回滚事务或重启线程。

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1;
std::mutex mutex2;

void thread1() {
    mutex1.lock();
    std::cout << "Thread 1: Holding mutex1, waiting for mutex2." << std::endl;
    mutex2.lock();
    std::cout << "Thread 1: Holding both mutexes." << std::endl;
    mutex2.unlock();
    mutex1.unlock();
}

void thread2() {
    mutex2.lock();
    std::cout << "Thread 2: Holding mutex2, waiting for mutex1." << std::endl;
    mutex1.lock();
    std::cout << "Thread 2: Holding both mutexes." << std::endl;
    mutex1.unlock();
    mutex2.unlock();
}

int main() {
    std::thread t1(thread1);
    std::thread t2(thread2);
    t1.join();
    t2.join();
    return 0;
}

在这个例子中,thread1thread2 都试图先锁定 mutex1,然后锁定 mutex2。如果两个线程几乎同时运行,它们将相互等待对方释放资源,从而导致死锁。

智能锁(std::lock)

std::lock 是 C++11 引入的一个函数,用于一次性锁定多个互斥锁(mutexes),从而避免死锁。它确保所有提供的互斥锁都被锁定,或者在遇到异常时,已经锁定的互斥锁会被解锁,这通过 RAII(资源获取即初始化)风格保证。

#include <mutex>
#include <thread>

std::mutex mtx1, mtx2;

void threadSafeFunction() {
    std::lock(mtx1, mtx2); // 一次性锁定两个互斥锁,要么同时锁,要么同时不锁
    std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);

    // 临界区代码,现在可以安全地访问共享资源
    // 无需手动解锁,lock1 和 lock2 在作用域结束时自动解锁
}

在这个例子中,std::lock_guardstd::adopt_lock 参数告诉 std::lock_guard,互斥锁已经被锁定,并且 std::lock_guard 应该在析构时解锁它们。

注意事项

  1. 避免死锁:使用 std::lock 可以减少死锁的风险,因为它确保所有互斥锁同时被锁定或解锁。

  2. 异常安全std::lockstd::lock_guard 结合使用,可以确保即使在抛出异常的情况下,互斥锁也能被正确解锁。

  3. 性能考虑:虽然 std::lock 提供了方便的一次性锁定多个互斥锁的能力,但它可能比单独锁定每个互斥锁有更高的性能开销。在性能敏感的应用中,应谨慎使用。

  4. 可扩展性std::lock 可以处理任意数量的互斥锁,这使得它在需要锁定多个资源时非常灵活。

通过使用 std::lockstd::lock_guard,你可以编写更安全、更易于维护的并发代码。(谨慎使用)

智能锁(unique_lock)

std::unique_lock 是 C++11 引入的一个类模板,它提供了一种灵活的方式来管理互斥量(mutex)。与 std::lock_guard 相比,std::unique_lock 更加灵活,允许在不同的作用域和不同的锁定策略之间进行选择。以下是 std::unique_lock 的一些主要特点和用法:

特点

  1. 灵活性std::unique_lock 可以在构造时选择是否锁定互斥量,支持手动锁定和解锁,允许条件变量的等待,以及在等待条件变量时自动解锁和重新锁定。

  2. 避免死锁:由于 std::unique_lock 的 RAII 特性,确保在作用域结束时自动解锁,降低了因忘记解锁而引起死锁的风险。

  3. 可以延迟锁定:你可以在构造 unique_lock 时不锁定互斥量,并在后面需要时再手动锁定。

  4. 可移动std::unique_lock 是可移动的,但不可复制,这使得它可以在线程间安全地转移锁的所有权。

adopt_lock
  • 延迟锁定:可以在构造时选择不锁定互斥量,并在需要时手动锁定。

std::adopt_lock 是 C+++11 引入的一个标签类型,用于指示 std::unique_lockstd::lock_guard 在构造时不要尝试去锁定传入的互斥锁(mutex),因为互斥锁已经被当前线程锁定。这个标签主要用于在已经手动锁定互斥锁的情况下,将互斥锁的管理权转移给 std::unique_lockstd::lock_guard 对象,以利用它们的异常安全保证(RAII)。

使用 std::adopt_lock 时,你必须确保在构造 std::unique_lockstd::lock_guard 之前,互斥锁已经被当前线程锁定。这通常用于避免在已经手动锁定互斥锁的情况下,再次尝试锁定互斥锁,从而提高效率。

#include <mutex>
#include <thread>
#include <iostream>

std::mutex mtx;

void print_thread_id(int id) {
    mtx.lock(); // 手动锁定互斥锁
    std::lock_guard<std::mutex> lck(mtx, std::adopt_lock); // 告诉 lock_guard 互斥锁已经被锁定
    std::cout << "thread #" << id << '\n';
    mtx.unlock(); // 手动解锁互斥锁
}

int main() {
    std::thread threads[10];
    for (int i = 0; i < 10; ++i)
        threads[i] = std::thread(print_thread_id, i + 1);
    for (auto& th : threads) th.join();
    return 0;
}
try_to_lock
  • 递归锁定:允许同一个线程多次锁定同一个互斥量。

std::unique_lock 提供了一种灵活的方式来管理互斥锁(mutex),它允许你尝试锁定互斥锁而不必真正锁定它。try_to_lockstd::unique_lock 的一个构造参数选项,用于实现这种尝试锁定的行为。

当你使用 std::try_to_lock 作为 std::unique_lock 的构造函数参数时,互斥锁不会立即被锁定。相反,std::unique_lock 会尝试锁定互斥锁,如果互斥锁已经被其他线程锁定,则不会阻塞调用线程,而是立即返回。这允许你实现非阻塞的锁定机制。

        

#include <mutex>
#include <thread>
#include <iostream>

std::mutex mtx;

void threadSafeFunction() {
    std::unique_lock<std::mutex> lock(mtx, std::try_to_lock);
    
    if (lock.owns_lock()) {
        // 如果成功锁定,执行临界区代码
        std::cout << "Thread " << std::this_thread::get_id() << " has the lock." << std::endl;
    } else {
        // 如果未能锁定,可以执行其他操作或重试
        std::cout << "Thread " << std::this_thread::get_id() << " could not get the lock." << std::endl;
    }
}

int main() {
    std::thread t1(threadSafeFunction);
    std::thread t2(threadSafeFunction);
    
    t1.join();
    t2.join();
    
    return 0;
}
defer_lock

在 C++ 中,std::unique_lock 提供了一种灵活的互斥锁管理方式,它允许延迟锁定(defer locking)。std::defer_lockstd::unique_lock 的构造函数的一个可选参数,用于指示 std::unique_lock 的实例在初始化时不立即锁定互斥锁,而是将其保持在未锁定状态。这使得开发者可以在之后的某个时刻显式地调用 lock 方法来获取锁。

使用 std::defer_lock 的优点包括:

  1. 延迟锁定:允许开发者在构造 std::unique_lock 实例后,根据需要决定何时锁定互斥锁。
  2. 避免死锁:通过延迟锁定,可以减少在获取多个锁时发生死锁的风险。
  3. 灵活性:提供了在尝试锁定之前执行其他操作的能力,例如检查条件或执行初始化。
#include <mutex>
#include <thread>
#include <iostream>

std::mutex mtx;

void threadSafeFunction() {
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 延迟锁定

    // 执行一些不需要互斥的操作
    // ...

    // 现在锁定互斥锁
    lock.lock();
    // 临界区代码,现在可以安全地访问共享资源
    std::cout << "Thread " << std::this_thread::get_id() << " has the lock." << std::endl;

    // 离开作用域时自动解锁
}

int main() {
    std::thread t1(threadSafeFunction);
    std::thread t2(threadSafeFunction);
    t1.join();
    t2.join();
    return 0;
}
release

std::unique_lockrelease 成员函数用于释放与互斥锁(mutex)的关联,但不解锁互斥锁。当你调用 release 方法后,std::unique_lock 实例将不再管理互斥锁,这意味着它不会在析构时解锁互斥锁。这允许你将互斥锁的控制权显式地转移给其他 std::unique_lock 实例或手动管理互斥锁。

使用 release 方法后,你需要确保互斥锁在不再需要时被正确解锁,因为 std::unique_lock 不再负责解锁操作。

#include <mutex>
#include <thread>
#include <iostream>

std::mutex mtx;

void transferLock() {
    std::unique_lock<std::mutex> lock(mtx);
    // 临界区代码
    std::cout << "Thread " << std::this_thread::get_id() << " has the lock." << std::endl;

    // 释放互斥锁的控制权,但不解锁
    lock.release();
}

void manualUnlock() {
    // 直接使用已经释放的互斥锁
    mtx.unlock();
    std::cout << "Thread " << std::this_thread::get_id() << " manually unlocked the mutex." << std::endl;
}

int main() {
    std::thread t1(transferLock);
    std::thread t2(manualUnlock);
    t1.join();
    t2.join();
    return 0;
}

 单例设计模式分析与解决

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <list>
using  namespace std;

class MyCls
{
public:
	// 提供一个全局访问点,用于获取类的唯一实例
	static MyCls* m_inst;

	static MyCls* instance()
	{
		if (m_inst == nullptr)
		{
			m_inst = new MyCls();
			static CGar c1;
		}
		return m_inst;
	}
	class CGar
	{
	public:
		~CGar()
		{
			if (MyCls::m_inst)
			{
				delete MyCls::m_inst;
				MyCls::m_inst = NULL;
				cout << "单例数据被释放了" << endl;
			}
		}
	};

	static MyCls* instance1()
	{
		static MyCls* inst = new MyCls;
		return inst;
	}


};
MyCls* MyCls::m_inst = NULL;

int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	cout << MyCls::instance() << endl;
	cout << MyCls::instance() << endl;

	cout << MyCls::instance1() << endl;
	cout << MyCls::instance1() << endl;

	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl;

	

	return 0;
}



多线程时问题与解决

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <list>
using  namespace std;

class MyCls
{
public:
	// 提供一个全局访问点,用于获取类的唯一实例
	static MyCls* m_inst;

	static MyCls* instance()
	{
		if (m_inst == nullptr)
		{
			this_thread::sleep_for(std::chrono::microseconds(1000));
			static CGar c1;
			m_inst = new MyCls();
		}
		return m_inst;
	}
	class CGar
	{
	public:
		~CGar()
		{
			if (MyCls::m_inst)
			{
				delete MyCls::m_inst;
				MyCls::m_inst = NULL;
				cout << "单例数据被释放了" << endl;
			}
		}
	};

	static MyCls* instance1()
	{
		this_thread::sleep_for(std::chrono::microseconds(1000));
		//方式一
		//static MyCls* inst = new MyCls; //在C++11之后,这种写法是安全的
		//return inst;
		//方式二
		static MyCls inst;
		return &inst;
	}


};
MyCls* MyCls::m_inst = NULL;

void mythread()
{	
	cout << "mythread start" << endl;
	MyCls* me =  MyCls::instance1();
	cout << "me = " << me;
	cout << "mythread end" << endl;
}

int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	//cout << MyCls::instance() << endl;
	//cout << MyCls::instance() << endl;

	std::thread obj1(mythread);
	std::thread obj2(mythread);

	obj1.join();
	obj2.join();


	//cout << MyCls::instance1() << endl;
	//cout << MyCls::instance1() << endl;

	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl;

	

	return 0;
}



call_once

// ConsoleApplication10.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <mutex>
#include <cstdint>
#include <list>
using  namespace std;

std::mutex g_mut;

std::once_flag g_flag;


class MyCls
{
public:
	// 提供一个全局访问点,用于获取类的唯一实例
	static MyCls* m_inst;

	static MyCls* instance()
	{
		if (m_inst != nullptr)
			return m_inst;  //使用双检索进行判断。	
		std::unique_lock<std::mutex> mymut(g_mut); //自动加锁,出作用域自动释放。
		if (m_inst == nullptr)
		{
			this_thread::sleep_for(std::chrono::microseconds(1000));
			static CGar c1;
			m_inst = new MyCls();
		}
		return m_inst;
	}
	class CGar
	{
	public:
		~CGar()
		{
			if (MyCls::m_inst)
			{
				delete MyCls::m_inst;
				MyCls::m_inst = NULL;
				cout << "单例数据被释放了" << endl;
			}
		}
	};

	static MyCls* instance1()
	{
		this_thread::sleep_for(std::chrono::microseconds(1000));
		//方式一
		//static MyCls* inst = new MyCls; //在C++11之后,这种写法是安全的
		//return inst;
		//方式二
		static MyCls inst;
		return &inst;
	}


};
MyCls* MyCls::m_inst = NULL;

void mythread()
{	
	cout << "mythread start" << endl;
	MyCls* me =  MyCls::instance();
	cout << "me = " << me;
	cout << "mythread end" << endl;
}

int main()
{
	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl << endl;
	//cout << MyCls::instance() << endl;
	//cout << MyCls::instance() << endl;

#if 1
	std::thread obj1(mythread);
	std::thread obj2(mythread);
	obj1.join();
	obj2.join();
#endif

	//std::call_once(std::once_flag::once_flag,mythread);//call_once保证函数只调用一次,具备互斥量能力
	std::call_once(g_flag, mythread);
	std::call_once(g_flag, mythread);



	//cout << MyCls::instance1() << endl;
	//cout << MyCls::instance1() << endl;

	cout << "=========================================== " << " threadid = " << this_thread::get_id() << endl;

	

	return 0;
}



recursive_mutex

在 C++11 及以后的版本中,std::recursive_mutex 是一个模板类,它提供了递归互斥锁的功能。与标准互斥锁 std::mutex 不同的是,std::recursive_mutex 允许同一个线程多次获取同一个锁,这在某些情况下可以简化代码逻辑并避免死锁的递归问题。

特点

  1. 递归锁定:同一个线程可以多次锁定 std::recursive_mutex,每次锁定都会增加锁的计数。只有当线程解锁相同次数后,锁才会真正释放。

  2. 避免死锁:由于 std::recursive_mutex 允许同一个线程多次获取锁,因此可以避免在自旋逻辑中引入的死锁问题。

  3. 条件变量std::recursive_mutex 可以与 std::condition_variable 一起使用,用于线程间的同步。

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

void recursive_lock_function(std::recursive_mutex& mtx) {
    std::lock_guard< std::recursive_mutex> lock(mtx); // 第一次锁定
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    {
        std::lock_guard< std::recursive_mutex> lock(mtx, std::adopt_lock); // 第二次锁定,采用 adopt_lock
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    // 锁在 lock_guard 析构函数中自动释放
}

int main() {
    std::recursive_mutex mtx;
    std::thread t1(recursive_lock_function, std::ref(mtx));
    std::thread t2(recursive_lock_function, std::ref(mtx));

    t1.join();
    t2.join();

    return 0;
}

在这个例子中,recursive_lock_function 函数展示了如何使用 std::recursive_mutex。函数首先使用 std::lock_guard(它是一个 RAII 封装)来锁定互斥锁,然后在一个局部块中再次锁定同一个互斥锁,这次使用 std::adopt_lock 来获取已经由外部 std::lock_guard 锁定的互斥锁。

注意事项

  1. 锁计数std::recursive_mutex 内部维护一个锁计数,你需要确保每个 lock 调用都有相应的 unlock 调用,以避免死锁计数错误。

  2. 线程安全:尽管 std::recursive_mutex 允许递归锁定,但它仍然是线程安全的,可以被不同线程安全地锁定和解锁。

  3. 性能考虑:递归锁可能会增加锁的开销,因为每次锁定都需要更新锁计数。在不需要递归特性的情况下,使用 std::mutex 可能更高效。

  4. 避免滥用:递归锁应该谨慎使用,只在确实需要时才使用,以避免不必要的复杂性和潜在的性能问题。

std::recursive_mutex 在需要在同一个线程中多次锁定同一互斥锁的情况下非常有用,例如,在实现嵌套递归数据结构或在递归算法中。

timed_mutex

在 C++ 中,std::timed_mutex 是一个提供定时锁定功能的互斥锁。它允许你在尝试获取锁的时候设置一个超时时间,如果在指定的时间内未能获取到锁,就会返回一个错误码,而不是阻塞等待。这可以避免死锁的发生,并且使得线程有机会在超时后执行其他任务。

std::timed_mutex 提供了几个关键的成员函数来进行定时锁定:

  1. try_lock_for:尝试在指定的时间段内获取锁,如果成功则返回 true,否则返回 false

  2. try_lock_until:尝试在指定的时间点之前获取锁,如果成功则返回 true,否则返回 false

使用 std::timed_mutex 可以避免线程因为等待锁而被无限期阻塞,从而提高程序的响应性和健壮性。

#include <iostream>
#include <mutex>
#include <chrono>
#include <thread>

std::timed_mutex mtx;
//std::mutex mtx;

void do_work() {
	// 尝试在 100 毫秒内获取锁

	if (mtx.try_lock_for(std::chrono::milliseconds(1000))) //等待,拿到锁,或者等待超时没拿到就走下拉
	{
		std::cout << "Lock acquired" << std::endl;
		//mtx.unlock(); 不解锁的话,第一个线程就会拿到锁,第二个拿不到锁
	}
	else {
		std::cout << "Failed to acquire lock" << std::endl;
	}
}

int main() {
	std::thread t1(do_work);
	std::thread t2(do_work);

	t1.join();
	t2.join();

	return 0;
}

recursive_timed_mutex

 和recursive_mutex类似,就是可以多次加锁的区别。

#include <iostream>
#include <mutex>
#include <chrono>
#include <thread>

//std::recursive_mutex mtx;
std::recursive_timed_mutex mtx;


void do_work() {
	// 尝试在 100 毫秒内获取锁
	
	if (mtx.try_lock_for(std::chrono::milliseconds(1000))) //等待,拿到锁,或者等待超时没拿到就走下拉
	{
		std::cout << "Lock acquired" << std::endl;
		//mtx.unlock(); 不解锁的话,第一个线程就会拿到锁,第二个拿不到锁
	}
	else {
		std::cout << "Failed to acquire lock" << std::endl;
	}
}

int main() {
	std::thread t1(do_work);
	std::thread t2(do_work);

	t1.join();
	t2.join();

	return 0;
}

其他线程使用可以参数我主页中的 C语言:高级并发操作(线程)文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

only-lucky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值