C++多线程2

weakcore / 2024-01-18 / 原文

多线程通信与同步

1 多线程状态

1.1 线程状态说明:

  • 初始化 Init: 该进程正在被创建
  • 就绪 Ready :该线程在就绪列表中,等待CPU调度。
  • 运行 Running : 该新城正在运行。
  • 阻塞 Blocked :该线程备注色挂起,Block状态包括:pend(锁,事件,信号量等阻塞),suspend(主动pend),delay(演示阻塞),pendtime(因为所,事件,信号量时间等超时等待)。
  • 退出 Exit :该线程运行结束,等待父线程回收其控制块资源(不包含堆资源)。
    状态转换图如下:

1.1.2 竞争状态(Race Condition)和临界区(Critical Section)

竞争状态(Race Condition):
多线程同时读写共享数据。
临界区 (Critical Section):
读写共享的代码片段。
避免竞争状态策略: 对临界区进行保护,同时只有一个进程能够进入临界区。

1.2 互斥体和锁mutex

1.2.1 互斥锁 mutex

  • lock(),try_lock()
  • unlock()
    代码演示:
#include<thread>
#include<iostream>
#include <mutex>
using namespace std;
static mutex m;
void thread_main() {
   m.lock();//其他线程阻塞等待
   cout << "********************" << endl;
   cout << "1" << endl;
   cout << "1" << endl;
   cout << "1" << endl;
   cout << "1" << endl;
   cout << "********************" << endl;
   m.unlock();
}
int main() {
   for(int i=0;i<10;i++)
   
   {
   	thread th(thread_main);
   	th.detach();
   }
   return 0;
}

如果不加锁将会出现对临界区资源竞争的情况,无法输出整块内容。而加锁之后,可以完整的输出。
当我们进行加锁操作时,未获得锁的进程将会排队阻塞等待。如果忘记解锁,将会导致死锁问题,另外我们希望临界区代码越小越好,以防止资源的过度浪费。
此外,可以使用try_lock(),尝试对临界区进行加锁。子进程可以定时对所进行访问,当没有获取到锁时,进程不必阻塞等待。在进行try_lock()操作时也需要耗费系统资源。因此使用try_lock()时需要注意系统资源的释放(sleep),防止进程不断询问造成资源的耗尽。

1.2.2 互斥锁的坑_线程抢占不到资源

static mutex m;

void threadtest(int i) {
   for(;;)
   {
   	m.lock();
   	cout << i << "[in]" << endl;
   	this_thread::sleep_for(1s);
   	m.unlock();
   	//this_thread::sleep_for(1s);
   }

}
int main() {
   for (int i = 0; i < 5; i++) {
   	thread th(threadtest,i);
   	th.detach();
   }
   getchar();
}

代码运行结果如下:

通过图中我们可以看到一个线程抢到锁之后,其他线程就不能抢到锁。这是因为线程在处理循环事务时,在解锁和加锁之间线程没有其他事务,这导致系统还没来的及释放资源,就已经重新加锁。
而当我们在加锁和解锁之间添加其他事务时

static mutex m;

void threadtest(int i) {
	for(;;)
	{
		m.lock();
		cout << i << "[in]" << endl;
		this_thread::sleep_for(1s);
		m.unlock();
		this_thread::sleep_for(1s);
	}

}
int main() {
	for (int i = 0; i < 5; i++) {
		thread th(threadtest,i);
		th.detach();
	}
	getchar();
}

代码运行结果如下:

我们在解锁和加锁中间睡眠一秒,这样可以防止解锁之后立马加锁。

1.2.3 超时锁应用 timed_mutex避免长时间死锁

  • 可以记录锁获取情况,多次超时,可以记录日志,获取错误情况。