资源竞争
当进程中的多个线程,同时读取一块内存数据,与此同时其中一个或多个线程修改了这块内存数据。这样就会导致不可预期的结果
因为线程不安全引起的错误往往非常难发现,因为这种现象是不能稳定复现的。
下面举个例子:
#include <stdio.h>
#include <string.h>
#include <pthread.h>
int g = 0;
//100000000-200000000
void *pf(void *arg){
int i;
for(i=0;i<100000000;i++){
g = g+1;//读内存 加法指令 写内存
}
}
int main(){
pthread_t ids[2];
int ret1 = pthread_create(&ids[0],NULL,pf,NULL);
int ret2 = pthread_create(&ids[1],NULL,pf,NULL);
if(ret1 != 0 || ret2 != 0){
printf("pthread_create:%s\n",strerror(ret1!=0?ret1:ret2));
return -1;
}
int i;
for(i=0;i<2;i++){
pthread_join(ids[i],NULL);
}
printf("%d\n",g);
return 0;
}
输出结果:
线程同步
当多个线程同时访问其所共享的进程资源时,需要相互协调,以防止出现数据不一致、不完整的问题。这就叫线程同步。
1. 互斥锁
特点:对于读者和写者来说。只要有一方获取了锁,另一方则不能继续获取,进而执行 临界区代码。
1.互斥锁类型
1.普通锁 (PTHREAD_MUTEX_NORMAL) 互斥锁默认类型。当一个线程对一个普通锁加锁以后,其余请求该锁的线程将形成一个 等待队列,并在该锁解锁后按照优先级获得它,这种锁类型保证了资源分配的公平性。一个 线程如果对一个已经加锁的普通锁再次加锁,将引发死锁;对一个已经被其他线程加锁的普 通锁解锁,或者对一个已经解锁的普通锁再次解锁,将导致不可预期的后果。
2.检错锁(PTHREAD_MUTEX_ERRORCHECK) 一个线程如果对一个已经加锁的检错锁再次加锁,则加锁操作返回EDEADLK;对一个已 经被其他线程加锁的检错锁解锁或者对一个已经解锁的检错锁再次解锁,则解锁操作返回 EPERM;
3.嵌套锁(PTHREAD_MUTEX_RECURSIVE) 该锁允许一个线程在释放锁之前多次对它加锁而不发生死锁;其他线程要获得这个锁, 则当前锁的拥有者必须执行多次解锁操作;对一个已经被其他线程加锁的嵌套锁解锁,或者 对一个已经解锁的嵌套锁再次解锁,则解锁操作返回EPERM
4.默认锁(PTHREAD_MUTEX_ DEFAULT) 一个线程如果对一个已经加锁的默认锁再次加锁,或者虽一个已经被其他线程加锁的默 认锁解锁,或者对一个解锁的默认锁解锁,将导致不可预期的后果;这种锁实现的时候可能 被映射成上述三种锁之一;
//有两种方法创建互斥锁,静态方式和动态方式。
//POSIX定义了一个宏 PTHREAD_MUTEX_INITIALIZER 来静态初始化互斥锁,方法如下:
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//在LinuxThreads实现中,pthread_mutex_t是一个结构,而 PTHREAD_MUTEX_INITIALIZER则是一个结构常量。
//动态方式是采用pthread_mutex_init()函数来初始化互斥锁,API定义如下:
int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);
//其中mutexattr用于指定互斥锁属性(见上),如果为NULL则使用缺省属性,PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。
int pthread_mutex_lock (pthread_mutex_t* mutex);
int pthread_mutex_unlock (pthread_mutex_t* mutex);
int pthread_mutex_destroy (pthread_mutex_t* mutex);
1)互斥量被初始化为非锁定状态;
2)线程1调用pthread_mutex_lock函数,立即返回,互斥量呈锁定状态;
3)线程2调用pthread_mutex_lock函数,阻塞等待;
4)线程1调用pthread_mutex_unlock函数,互斥量呈非锁定状态;
5)线程2被唤醒,从pthread_mutex_lock函数中返回,互斥量呈锁定状态;
范例:
#include <stdio.h>
#include <string.h>
#include <pthread.h>
int g = 0;
pthread_mutex_t lock;
//100000000-200000000
void *pf(void *arg){
int i;
for(i=0;i<100000000;i++){
pthread_mutex_lock(&lock);
g = g+1;//读内存 加法指令 写内存
pthread_mutex_unlock(&lock);
}
}
int main(){
pthread_t ids[2];
int ret = pthread_mutex_init(&lock,NULL);
if(ret != 0){
printf("pthread_mutex_init:%s\n",strerror(ret));
}
int ret1 = pthread_create(&ids[0],NULL,pf,NULL