C++多线程2
多线程通信与同步
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避免长时间死锁
- 可以记录锁获取情况,多次超时,可以记录日志,获取错误情况。