[C++11 多线程同步] --- 线程同步概述
Posted Overboom
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[C++11 多线程同步] --- 线程同步概述相关的知识,希望对你有一定的参考价值。
1 线程调度的几个基本知识点
多线程并发执行时有很多同学捋不清楚调度的随机性会导致哪些问题,要知道如果访问临界资源不加锁会导致一些突发情况发生甚至死锁。
关于线程调度,需要深刻了解以下几个基础知识点:
- 调度的最小单位是轻量级进程或者线程;
- 每个线程都会分配一个时间片,时间片到了就会执行下一个线程;
- 线程的调度有一定的随机性,无法确定什么时候会调度;
- 在同一个进程内,创建的所有线程除了线程内部创建的局部资源,进程创建的其他资源所有线程共享; 比如:主线程和子线程都可以访问全局变量,打开的文件描述符等。
2 为什么需要线程同步
假设有 4 个线程 A、B、C、D,当前一个线程 A 对内存中的共享资源进行访问的时候,其他线程 B, C, D 都不可以对这块内存进行操作,直到线程 A 对这块内存访问完毕为止,B,C,D 中的一个才能访问这块内存,剩余的两个需要继续阻塞等待,以此类推,直至所有的线程都对这块内存操作完毕。 线程对内存的这种访问方式就称之为线程同步,通过对概念的介绍,我们可以了解到所谓的同步并不是多个线程同时对内存进行访问,而是按照先后顺序依次进行的。
下面看一段代码说明为什么需要线程同步,两个线程对一个共享数据进行++操作并且输出出来,代码如下:
#include <iostream>
#include <thread>
#include <unistd.h>
using namespace std;
int share = 0; //共享变量
void thread1()
for (int i = 0; i < 100; i++)
int tmp = share;
tmp++;
usleep(10);
share = tmp;
cout << "thread1: share is " << share << endl;
void thread2()
for (int i = 0; i < 100; i++)
int tmp = share;
tmp++;
usleep(200);
share = tmp;
cout << "thread2: share is " << share << endl;
int main()
thread task1(thread1);
thread task2(thread2);
task1.join();
task2.join();
随机运行一次,结果如下:
可以看到,不但出现两个线程读取的变量值一样的现象(我们当然期望的是每一行都是一个唯一的数字并且有一个换行),还出现了cout的内容包括数字和换行符位置也有些错乱。主要原因是share和cout的缓冲区是thread1和thread2共享的,由于两个线程同时运行,便可能将一个已经修改的值读取,或者将另一个线程已经读取但是未修改的值进行读取,还有可能将另一个线程已经放入缓冲区的内容输出。当然,这个输出是不符合我们预期的!
3 线程同步的四种方式
对于多个线程访问共享资源出现数据混乱的问题,需要进行线程同步。常用的线程同步方式有四种:互斥锁、读写锁、条件变量、信号量。所谓的共享资源就是多个线程共同访问的变量,这些变量通常为全局数据区变量或者堆区变量,这些变量对应的共享资源也被称之为临界资源。
找到临界资源之后,再找和临界资源相关的上下文代码,这样就得到了一个代码块,这个代码块可以称之为临界区。确定好临界区(临界区越小越好)之后,就可以进行线程同步了。
线程同步的大致处理思路是这样的:
- 在临界区代码的上边,添加加锁函数,对临界区加锁。
哪个线程调用这句代码,就会把这把锁锁上,其他线程就只能阻塞在锁上了。 - 在临界区代码的下边,添加解锁函数,对临界区解锁。
出临界区的线程会将锁定的那把锁打开,其他抢到锁的线程就可以进入到临界区了。 - 通过锁机制能保证临界区代码最多只能同时有一个线程访问,这样并行访问就变为串行访问了。
以上是关于[C++11 多线程同步] --- 线程同步概述的主要内容,如果未能解决你的问题,请参考以下文章