一.线程的同步与互斥
线程的同步:实际上是指线程间的通信。当有线程访问公共资源时,就必须确定另一个已经完成了某些操作,它才可以在执行,这就叫线程的同步。
线程的互斥:在多线程运行的环境下,线程拥有公共的资源,但是某个时刻只能有一个线程对这种资源进行访问。这种资源被称为临界资源。临界区就是访问临界资源执行的代码。
例如:桌子上有一个盘子,当妈妈在盘子放了水果,儿子才可以去拿。这时的盘子就相当于是一个公共资源,妈妈和儿子不能同时访问。而妈妈只有等儿子拿走了放的水果,她才可以再次进行放置,否则就等儿子来拿水果。儿子要来拿水果也是同样的道理,当妈妈放好了水果,他才可以去取,否则就等待妈妈放好水果。
要深入了解线程的同步与互斥 引入几个
A.mutex(互斥量)
互斥量跟临界区相似,只是拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何时刻下共享资源都不会同时被多个线程访问。当前占用资源的的线程在完成任务后就将拥有的互斥对象交出,以便其他线程得到后访问资源。
+1操作:从内存读变量到寄存器->寄存器的值加1->将寄存器的值写回内存
举一个例子:
1 #include<stdio.h> 2 #include<pthread.h> 3 static int g_count=0; 4 void *print_bug(void*arg) 5 { 6 //int index=0; 7 int tmp=0; 8 while(tmp++<10) 9 { 10 printf("this is thread %d,count is:%d\n",(int)arg,++g_count); 11 //g_count=index++; 12 13 } 14 15 16 17 18 } 19 20 int main() 21 { 22 pthread_t tid1,tid2; 23 pthread_create(&tid1,NULL,print_bug,(void*)1); 24 pthread_create(&tid2,NULL,print_bug,(void*)2); 25 void*ret=NULL; 26 pthread_join(tid1,&ret); 27 pthread_join(tid2,&ret); 28 29 30 31 return 0; 32 }
若放开inedex,运行结果:
这样的话两个线程会相互干扰,影响个g_count的值。
要解决上述问题就需用互斥量,即加锁。
加锁:
pthread_mutex lock=PTHREAD_MUTEX_INITIALIZER;
解锁:
pthread_mutex unlock=PTHREAD_MUTEX_INITIALIZER;
销毁锁:
pthread_mutex destroy(&lock);
所以解决以上问题就是在while循环中先加锁执行完输出再解锁。两个线程对g_count的操作就不会相互干扰了。
但使用了锁就有可能出现死锁的问题。先来了解什么是死锁?
1.死锁:可以把死锁定义为一组相互竞争系统资源或进行通信的进程间的“永久”阻塞。当一组进程中的每个进程都在等待某个事件(典型的情况是等待所请求的资源释放),而只有在这组进程中的其他被阻塞的进程才可以触发该事件,这时就称这组进程发生死锁。因为没有事件能够被触发,所以死锁是永久性的。
2.导致死锁的4个必要条件:
互斥:一次只有一个进程可以使用一个资源。其他进程不能访问已分配给其他进程的资源。
占有且等待:当一个进程等待其他进程时,继续占有已经分配的资源。
不可抢占:不能强行抢占进程已占有的资源。
循环等待:存在一个封闭的进程链,使得每个进程至少占有此链中下一个进程所需要的一个资源。
3.死锁的预防:
通过设置某些限制条件去破坏死锁的必要条件。
4.死锁的避免:
是在资源的的动态分配过程中,用某种方法防止系统进入不安全状态,从而避免死锁。
注:
系统的不安全状态:
其实进入了不安全状态仅说明当前情况下的资源分配出现不安全的因素,而随着时间的推移,资源的分配可能会发生变化的,原来占有临界资源的进程可能因为某些原因自己阻塞起来,并放弃已拥有的临界资源跑到阻塞队列后排队,这样原来请求这些临界资源的进程就有可能满足其需要而可以执行。