线程的同步与互斥,死锁

线程的同步与互斥

多个线程同时访问共享数据时可能会发生冲突,比如两个线程同时把一个全局变量加1,结果可能不是我们所期待的:

我们看这段代码的执行结果:

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

static int g_count=0;

void *thread(void *arg)

{

int index=0;

int tmp=0;

while(index++<5000)

{

tmp=g_count;

printf("this is thread %d,count is :%d\n",(int)arg,tmp);

g_count=tmp+1;

}

}

int main()

{

pthread_t tid1,tid2;

pthread_create(&tid1,NULL,thread,(void*)1);

pthread_create(&tid2,NULL,thread,(void*)2);

pthread_join(tid1,NULL);

pthread_join(tid2,NULL);

printf("the final value is %d\n",g_count);

return 0;

}

我们看它的执行结果:

我们创建了两个线程,各自把全局变量g_count加了5000次,结果理论上应该是10000,但其实不然,每次运行的结果都不一样,证明访问冲突了。为了解决这个问题,我们需引入互斥锁。获得锁的线程可以完成“读--修改--写”的操作,然后释放锁给其他线程,没有获得锁的线程只能等待,而不能访问共享数据,这样“读--修改--写”散步操作就成了原子操作,要么都执行,要么都不执行,不会执行到中间而被打断,这样就如我们所期待的了。

pthread_mutex_init函数对mutex做初始化,它可以被pthread_mutex_destroy销毁。如果mutex变量是静态分配的,也可以用宏定义PTHREAD_MUTEX_INITIALIZER来初始化,相当于pthread_mutex_init初始化并且attr参数为NULL。

加锁和解锁所需函数:

int pthread_mutex_lock(pthread_mutex_t *mutex)

int pthread_mutex_trylock(pthread_mutex_t *mutex)

int pthread_mutex_unlock(pthread_mutex_t *mutex)

成功返回0,失败返回错误号。

一个线程可以调用int pthread_mutex_lock获得mutex,如果这时候另一个线程已经调用int pthread_mutex_lock获得了该mutex,则当前线程需要挂起等待,直到另一个线程调用pthread_mutex_unlock释放mutex,当前线程被唤醒,才能获得该mutex并继续执行。

如果一个线程既想获得锁,又不想挂起等待,可以调用 pthread_mutex_trylock,如果mutex已经被另一个线程获得,则这个函数会返回EBUSY,而不会使线程挂起等待。

知道这些的话,我们重新修改以上代码:

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

static int g_count=0;

pthread_mutex_t mutex_lock=PTHREAD_MUTEX_INITIALIZER;

void *thread(void *arg)

{

int index=0;

int tmp=0;

while(index++<5000)

{

pthread_mutex_lock(&mutex_lock);

tmp=g_count;

printf("this is thread %d,count is :%d\n",(int)arg,tmp);

g_count=tmp+1;

pthread_mutex_unlock(&mutex_lock);

}

}

int main()

{

pthread_t tid1,tid2;

pthread_create(&tid1,NULL,thread,(void*)1);

pthread_create(&tid2,NULL,thread,(void*)2);

pthread_join(tid1,NULL);

pthread_join(tid2,NULL);

printf("the final value is %d\n",g_count);

return 0;

}

运行结果如下:

我们可以看到,经过我们加锁后,最后value的值是我们所期待的10000,加锁成功,成功实现了两个线程的互斥运行。

死锁产生的原因及四个必要条件

所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,他们都将无法再向前推进。

产生死锁的主要原因有:

1.系统资源不足;

2.进程运行推进的顺序不合适;

3.资源分配不当等;

如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就降低,否则,就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。

产生死锁的四个必要条件:

1.互斥条件:一个资源每次只能被一个进程使用;

2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;

3.不剥夺条件:进程已获得的资源,在使用完之前,不能强行剥夺;

4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系;

以上是产生死锁的四个必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

处理死锁的基本方法:

1.预防死锁:

该方法是通过设置某些限制条件,去破坏产生死锁四个必要条件中的一个或几个条件,来预防发生死锁。

2.避免死锁

不需事先采取各种限制措施去破坏产生死锁的四个必要条件,而是在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免发生死锁。

3.检测死锁

这种方法不需事先采取任何限制措施,也不必检查系统是否已经进入不安全区,而是允许系统在运行过程中发生死锁。但可以通过系统所设置的检测机构,及时的检测死锁的发生,并精确的确定与死锁有关的进程资源,然后采取适当措施,从系统中将已发生的死锁清除掉。

4.解除死锁

这是与检测死锁相配套的一种措施。当检测到发生死锁时,需将进程从死锁状态中解脱出来。常用的实施方法是撤消或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,一边继续运行。

死锁的解除与预防:

理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大程度的避免,预防和解除死锁。所以在系统设计,进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占用系统资源。此外,也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给与合理的规划。

时间: 2024-08-04 22:21:51

线程的同步与互斥,死锁的相关文章

线程概念及线程的同步与互斥

线程概念:它是运行在进程内部的的一个基本执行流,多线程的控制流程可以长期并存,一个进程中的数据段和代码段都是被该进程中的多个线程共享的,若定义一个函数,每个线程都可以调用,若定义一个全局变量,每个线程都可以访问. 线程还共享进程的以下内容:1.文件描述符表 2.当前的工作目录 3.用户id(uid)和组id(gid) 4.每种信号的处理方式. 但每个线程还必须有自己的私有部分:1.线程id 2.硬件上下文(硬件寄存器的值,栈指针等) 3.自己的栈空间(运行时的临时数据都要保存在自己的栈空间上)

java的线程问题同步与互斥

以前学过java的线程,但是当时对Tread的理解不是很深,对于里面的同步与互斥,生产者与消费者问题,理解的不够深入,这次又从新学习java的多线程,感觉对线程的理解更加的深入了,觉得有必要写下博客记录下. 本文原创,转载请著明:http://blog.csdn.net/j903829182/article/details/38420503 1.java实现线程的方法: 1.实现Runnable接口,重写run方法,通过Thread的start方法启动线程.这种方法可以实现资源的共享 2.继承T

Linux环境下线程的同步与互斥以及死锁问题

由于本次要讨论操作系统的死锁问题,所以必须先研究的是linux环境下的线程同步与互斥 先看下面的代码 大家猜想输出应该是什么呢? 结果是下面这个样子 好吧,似乎并没有什么区别... 那么下面再看这段代码(请无视并忽略屏蔽的内容...) 大家猜想正确的结果是什么呢?5000,10000? 好吧,或许你们都错了. 在运行了一段时间后,它的结果是这样的. 是不是又对又错? 为什么呢? 这就是因为程序中printf语句作用:本身是库函数,所以必须进行系统调用,必须进入内核进行切换,有很大概率形成数据的混

线程的同步与互斥(死锁的产生和避免)

可以知道,一条语句对一个变量进行+1操作,转成汇编指令共有三条:将这个变量从内存中取出:将其值加1:再将加后的结果放回内存:当一个进程中的两个线程同时进行这个操作时,本来期望的是将变量进行两次加1,但中途有可能当一个线程刚从内存中将变量取出就被切换暂停了,此时线程会保存硬件上下文,第二个线程将变量加1之后前面切出去的线程回来继续执行,这时保存的还是变量原来的值,再将变量加1,会发现变量的最终结果并没有加2而是只加了1,因此这种操作并不是原子的. -------------------------

生产者与消费者模式(线程的同步与互斥)

死锁产生的四个条件: 1.互斥使用(资源独占) 一个资源每次只能给一个进程使用 .2.不可强占(不可剥夺) 资源申请者不能强行的从资源占有者手中夺取资源,资源只能由占有者自愿释放 .3.请求和保持(部分分配,占有申请) 一个进程在申请新的资源的同时保持对原有资源的占有(只有这样才是动态申请,动态分配) .4.循环等待 存在一个进程等待队列 {P1 , P2 , - , Pn}, 其中P1等待P2占有的资源,P2等待P3占有的资源,-,Pn等待P1占有的资源,形成一个进程等待环路 生产者:生产数据

Linux下线程的同步与互斥

一.线程的互斥 多个线程同时访问共享数据时可能会冲突,跟之前信号量的可重如性是同样的问题.如两个线程都要把某个全局变量增加1,这个操作在某平台需要三条指令完成: 1. 从内存读变量值到寄存器 2. 寄存器的值加1 3. 将寄存器的值写回内存 如下程序就会产生问题: 我们创建两个线程,每把g_count增加5000次,正常情况下最后g_count应该等于10000,但事实上每次运行该程序的结果都不一样. 对于多线程的程序,访问冲突的问题是很普遍的,解决的办法是引入互斥锁(Mutex,Mutual

线程的同步之互斥量

互斥量: 当多个线程共享相同的内存时,需要每一个线程看到相同的视图.当一个线程修改变量时,而其他线程也可以读取或者修改这个变量,就需要对这些线程同步,确保他们不会访问到无效的变量 在变量修改时间多于一个存储器访问周期的处理器结构中,当存储器的读和写这两个周期交叉时,这种潜在的不一致性就会出现.当然这与处理器相关,但是在可移植的程序中并不能对处理器做出任何假设 为了让线程访问数据不产生冲突,这要就需要对变量加锁,使得同一时刻只有一个线程可以访问变量.互斥量本质就是锁,访问共享资源前对互斥量加锁,访

Linux多线程编程——线程的同步与互斥

前言:无论是多线程编程还是多进程编程,控制好不同线程或不同进程之间同步和互斥问题是非常有必要的.同步是多个进程或线程共同完成某个任务,举例说,一个缓冲区的生产者和消费者问题,当生产者生产了一个商品时,等待的消费者就获得了一个消息知道可以去取走商品了,当消费者取走一个商品后,生产者就知道可以继续生产一个商品了,这是同步问题,所谓互斥问题,是指某个共享资源在一次操作中,只能被一个线程或进程占有,其他的线程或进程不能对它进行操作,比如对一个共享内存的读写操作,当一个进程对它写的时候,另一个进程就不能对

线程的同步与互斥(生产者与消费者模型)

一个进程中可以有多个线程,这些线程共享进程的资源,但当多个线程访问同一个资源时,在并不能保证操作是原子的情况下,就会产生冲突而使数据最终的结果不准确,像上次我们提到的将一个数进行加1操作需要三步:将数据从内存中取出:将数据加1:再将数据放回内存中,当多个线程并发执行这一操作时,有可能一个线程刚将数据从内存中取出就被临时切换出去了,这时别的线程进行加1操作,而当被切出去的线程重新回来继续执行加1操作时,这个数据已经改变了但是它拿到的还是原来的值,再进行加1放回内存时,结果就和我们所期望达到的不一样