线程同步的概念
线程同步,指一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。同时其它线程为保证数据一致性,不能调用该功能。
线程同步的例子
创建两个线程,让两个线程共享一个全局变量int number, 然后让每个线程数5000次数,看最后打印出这个number值是多少?
线程A代码段:
线程B代码段:
代码片段说明:
代码中使用调用usleep是为了让两个子线程能够轮流使用CPU,避免一个子线程在一个时间片内完成5000次数数。对number执行++操作,使用了中间变量cur是为了尽可能的模拟cpu时间片用完而让出cpu的情况。
测试结果:经过多次测试最后的结果显示,有可能会出现number值少于5000*2=10000的情况。
分析原因:
假如子线程A执行完了cur++操作,还没有将cur的值赋值给number失去了cpu的执行权,子线程B得到了cpu执行权,而子线程B最后执行完了number=cur,而后失去了cpu的执行权;此时子线程A又重新得到cpu的执行权,并执行number=cur操作,这样会把线程B刚刚写回number的值被覆盖了,造成number值不符合预期的值。
如何解决这个问题:
Linux中提供一把互斥锁mutex(也称之为互斥量)。每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。
资源还是共享的,线程间也还是竞争的,但通过“锁”就将资源的访问变成互斥操作,而后与时间有关的错误也不会再产生了。
线程1访问共享资源的时候要先判断锁是否锁着,如果锁着就阻塞等待;若锁是解开的就将这把锁加锁,此时可以访问共享资源,访问完成后释放锁,这样其他线程就有机会获得锁。
应该注意的是:图中同一时刻,只能有一个线程持有该锁,只要该线程未完成操作就不释放锁。使用互斥锁之后,两个线程由并行操作变成了串行操作,效率降低了,但是数据不一致的问题得到解决了。
互斥锁主要相关函数
pthread_mutex_t mutex;定义互斥锁
pthread_mutex_init(&mutex, NULL);互斥锁初始化
pthread_mutex_lock(&mutex);加锁
pthread_mutex_unlock(&mutex);解锁
pthread_mutex_destroy(&mutex);销毁互斥锁
#include <stdio.h>
#include<iostream>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
using namespace std;
#define NUM 20000
int number = 0;
//定义一把锁
pthread_mutex_t mutex;
void *mythread1(void *args)
{
int i = 0;
int n;
for (i = 0; i < NUM; i++)
{
//加锁
pthread_mutex_lock(&mutex);
n = number;
n++;
number = n;
cout << "[" << n << "]" << endl;
//解锁
pthread_mutex_unlock(&mutex);
}
}
void *mythread2(void *args)
{
int i = 0;
int n;
for (i = 0; i < NUM; i++)
{
//加锁
pthread_mutex_lock(&mutex);
n = number;
n++;
number = n;
cout << "[" << n << "]" << endl;
//解锁
pthread_mutex_unlock(&mutex);
}
}
int main()
{
int ret;
pthread_t thread1;
pthread_t thread2;
//互斥锁初始化
pthread_mutex_init(&mutex, NULL);
ret = pthread_create(&thread1, NULL, mythread1, NULL);
if(ret!=0)
{
cout << "pthread_create error, " << strerror(ret) << endl;
return -1;
}
ret = pthread_create(&thread2, NULL, mythread2, NULL);
if(ret!=0)
{
cout << "pthread_create error, " << strerror(ret) << endl;
return -1;
}
//等待线程结束
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
//释放互斥锁
pthread_mutex_destroy(&mutex);
return 0;
}
多跑几次代码之后结果还是40000,成功的使用互斥锁消除了线程同步问题。