多线程编程之互斥量

  最近在看POSIX多线程编程,想把自己看完之后还记得的只言片语记录一下,加深记忆。

  如果多个线程对同一个内存区域进行读写操作,可以使用互斥锁保证某个线程对该内存的操作具有原子性。

使用多线程是为了使程序的运行达到并发甚至并行的目的,互斥锁的使用使多个线程在临界区只能一个一个的运行。

临界区是指操作全局内存区域的那一块代码段。

  互斥量的初始化:

  pthread_t mutex = PTHREAD_MUTEX_INITIALIZER;  静态初始化

  pthread_mutex_init();动态初始化,使用动态初始化就需要配合pthread_mutex_destroy()释放互斥量

  互斥量的三个函数:

  pthread_mutex_lock(pthread_t *mutex);

  pthread_mutex_trylock(pthread_t *mutex);

  pthread_mutex_unlock(pthread_t *mutex);

上面的几个函数是凭印象写出来的,用的时候还需要自己在查一下。

  谈到互斥量就不可避免的要说到死锁,死锁产生的原因有很多,下面这种情况比较典型,A进程给1号资源加了锁,此时需要

2号资源,而B进程给2号资源加了锁,此时需要1号资源。这种情况下A等待B解锁2号资源,B等待A解锁一号资源,A和B发生了

死锁。

  要产生上面那种情况,首先需要多个共享资源,其次进程对共享资源的使用是互斥的,即同一时间只能有一个进程占有该资源(

互斥条件),一个进程已经占有某个资源,保持对该进程占有的情况下,又去获取其他资源(请求和保持条件),进程已获得的资源,在

未使用完之前,不可被剥夺,只能有自己使用完释放(不可剥夺条件)。

  避免死锁方法,要避免上述情况的死锁,可以采用顺序加锁法和回退算法。

  顺序加锁法,法如其名,对于共享资源1,2,3,线程A和B按照统一的顺序对他们加锁,即A是从1到3加锁,B也从1到三加锁,这样可以

避免A和B之间形成资源的环路等待。

  回退算法:A和B各自选一个方向对共享资源组进行加锁(方向不必相同),A首先对第一个资源进行加锁( pthread_mutex_lock() ),加锁

成功后对其他的资源一次执行试加锁( pthread_mutex_trylock() ),如果试加锁的资源已经被其他进程加锁,A进程将自己已经所占有的资源全

部解锁。这样的话需要A所占有的资源的其他进程就可以加锁成功,避免了死锁。

  在回退算法里面之所以对第一个资源进行直接加锁而不是试加锁,我认为是因为各进程都是按顺序进行加锁(别管顺序是否统一),如果第一

个锁就加锁不成功,那基本上是产生不了死锁了。也没必要一次解除它锁占有的资源(毕竟在加锁第一个资源之前该线程是没有占有资源的)。

  下面的程序可以来验证回退算法,backoff=0,两线程反方向加锁,产生死锁,backoff=1,两线程反方向加锁,使用了回退算法,不会发生死

锁。

#include <stdio.h>
#include <pthread.h>
#include <errno.h>

pthread_mutex_t mutex[3] = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER
};
void *thread_forward(void *);
void *thread_backward(void *arg);

int backoff = 1;
int main(int argc,char *argv[])
{
pthread_t forward,backward;

if (argc < 2)
{
printf("need two prm\n");
return 0;
}

backoff = atoi(argv[1]);
pthread_create(&forward,NULL,thread_forward,NULL);
pthread_create(&backward,NULL,thread_backward,NULL);

pthread_join(forward,NULL);
pthread_join(backward,NULL);
return 0;
}

void *thread_forward(void *arg)
{
int i = 0; 
int status = 0;

for (; i < 3; i++)
{
if (0 == i)
{
status = pthread_mutex_lock(&mutex[i]); 
if (status != 0)
{
printf("mutex_lock:%s",strerror(status));
return NULL;
}
continue;
}
if (!backoff)
{
status = pthread_mutex_lock(&mutex[i]); 
if (status != 0)
{
printf("mutex_lock:%s",strerror(status));
return NULL;
}
}
else
{
status = pthread_mutex_trylock(&mutex[i]);
if (status == EBUSY)
{
printf("locked at:%d\n",i);
for (; i >= 0; i--)
{
status = pthread_mutex_unlock(&mutex[i]); 
if (status != 0)
{
printf("mutex_unlock:%s",strerror(status));
return NULL;
}
}
}
}

usleep(1);   //不加sleep()函数可能看不出死锁的效果,因为一个线程可能在另一个线程执行之前就完成了所有互斥量的加锁解锁工作
}
for (i = 2; i >= 0; i--)
{
status = pthread_mutex_unlock(&mutex[i]);
if (status != 0)
{
printf("mutex_unlock:%s",strerror(status));
return NULL;
}
}
}

void *thread_backward(void *arg)
{
int i = 2; 
int status = 0;

for (; i >= 0; i--)
{
if (2 == i)
{
status = pthread_mutex_lock(&mutex[i]); 
if (status != 0)
{
printf("mutex_lock,backward:%s",strerror(status));
return NULL;
}
continue;
}
if (!backoff)
{
status = pthread_mutex_lock(&mutex[i]); 
if (status != 0)
{
printf("mutex_lock:%s",strerror(status));
return NULL;
}
}
else
{
status = pthread_mutex_trylock(&mutex[i]);
if (status == EBUSY)
{
printf("[bakckward],locked at:%d\n",i);
for (; i < 3; i++)
{
status = pthread_mutex_unlock(&mutex[i]); 
if (status != 0)
{
printf("mutex_unlock:%s",strerror(status));
return NULL;
}
}
}
}

usleep(1);
}
for (; i < 3; i++)
{
status = pthread_mutex_unlock(&mutex[i]);
if (status != 0)
{
printf("mutex_unlock:%s",strerror(status));
return NULL;
}
}
}

时间: 2024-08-08 09:18:44

多线程编程之互斥量的相关文章

C++11多线程编程之互斥量

+(UIImage*)createImageFromView:(UIView*)view { //obtain scale CGFloat scale = [UIScreen mainScreen].scale; 开始绘图,下面方法,第一个参数表示区域大小.第二个参数表示是否是非透明的.如果需要显示半透明效果,需要传NO,否则传YES.第三个参数就是屏幕密度了 UIGraphicsBeginImageContextWithOptions(CGSizeMake(view.frame.size.wi

多线程编程之互斥

一.互斥量(mutex) 多个线程同时访问共享数据时可能会冲突.比如某个线程把某个全局变量增加1,这个操作在某平台需要三条指令完成: (1)从内存读变量值到寄存器: (2)寄存器的值加1: (3)将寄存器的值写会内存. 假设两个线程在多处理器平台上同时执行这三条指令,则可能导致最后变量只加了1次,而不是2次. 程序如下: 1 #include<stdio.h> 2 #include<pthread.h> 3 #define MAX 5000 4 static int g_count

多线程编程:互斥

多线程共享一个进程的地址空间虽然线程间通信容易进行,但是多线程同时访问共享对象时需要引入同步和互斥机制. 1.线程间的互斥,引入互斥锁的目的是用来保证共享资源数据操作的完整性.互斥锁主要用来保护临界资源,每个邻界资源都由一个互斥锁来保护,任何时刻最多只能有一个线程能访问该资源.线程必须先获得互斥锁才能访问临界资源,访问完临界资源后释放该锁.如果无法获得锁,线程会阻塞知道获得锁为止. 2同步指的是多个任务按照约定的顺序相互配合完成一件事情,   1 #include<stdio.h>   2 #

Linux 多线程同步机制:互斥量、信号量、条件变量

互斥量:互斥量提供对共享资源的保护访问,它的两种状态:lock和unlock,用来保证某段时间内只有一个线程使用共享资源,互斥量的数据类型是pthread_mutex_t主要涉及函数:pthread_mutex_lock() pthread_mutex_trylock() pthread_mutex_unlock()Pthreaf_mutex_init() pthread_mutex_destroy()lock与unlock之间所锁定的区域为临界区域(如果只加锁不解锁程序会阻塞等待)信号量:信号

Linux多线程同步之互斥量和条件变量

1. 什么是互斥量 互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁.对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁.如果释放互斥锁时有多个线程阻塞,所以在该互斥锁上的阻塞线程都会变成可进行状态,第一个变成运行状态的线程可以对互斥量加锁,其他线程在次被阻塞,等待下次运行状态. pthread_mutex_t 就是POSIX对于mutex的实现. 函数名 参数 说明 pthread_mutex_init pthre

Linux下多线程编程之互斥锁、条件变量、信号量

1.进程创建 int pthread_create (pthread_t * thread_id, __const pthread_attr_t * __attr, void *(*__start_routine) (void *), void *__restrict __arg); 第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数. 一个实例: void *producer(void *args); pthread_

转--- 秒杀多线程第七篇 经典线程同步 互斥量Mutex

阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event> 前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用互斥量Mutex来解决这个问题. 互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行为非常相似,并且互斥量可以用于不同进程中的线程互斥访问资源.使用互斥量Mutex主要将用到四个函数.下面是这些函数

秒杀多线程第七篇 经典线程同步 互斥量Mutex

阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event> 前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用互斥量Mutex来解决这个问题. 互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行为非常相似,并且互斥量可以用于不同进程中的线程互斥访问资源.使用互斥量Mutex主要将用到四个函数.下面是这些函数

Linux系统编程@多线程编程

多线程编程 操作系统原理概念 时间片 进程状态 上下文: 对进程来说,就是进程的执行环境,具体就是各个变量和数据,包括所有的寄存器变量.打开的文件.内存信息等. 进程的写时复制:由于一般 fork后面都接着exec,所以,现在的 fork都在用写时复制的技术,顾名思意,就是,数据段,堆,栈,一开始并不复制,由父,子进程共享,并将这些内存设置为只读.直到父,子进程一方尝试写这些区域,则内核才为需要修改的那片内存拷贝副本.这样做可以提高 fork的效率. 线程函数的可重入性:所谓“重入”,常见的情况