Linux多线程编程(2)

来源:互联网 发布:mac切换大写字母 编辑:程序博客网 时间:2024/05/20 04:48

互斥量:

在上一篇博文中,介绍了多线程编程的一些基础知识,现在就来介绍一些线程同步的相关知识点:

一.互斥量

在多线程程序中同步访问手段可以是互斥量。程序员给某个对象加上一把“锁”,每次只允许一个线程去访问它。如果想对代码关键部分的访问进行控制,你必须在进入这段代码之前锁定一把互斥量,在完成操作之后再打开它。
互斥量函数有:
pthread_mutex_init    初始化一个互斥量
pthread_mutex_lock  给一个互斥量加锁
pthread_mutex_trylock  加锁,如果失败不阻塞
pthread_mutex_unlock  解锁
可以通过使用 pthread 的互斥接口保护数据,确保同一时间只有一个线程访问数据。互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所以在该互斥锁上的阻塞线程都会变成可进行状态,第一个变成运行状态的线程可以对互斥量加锁,其他线程在次被阻塞,等待下次运行状态。

互斥量用 pthread_mutex_t 数据类型来表示,在使用互斥量以前,必须首先对它进行初始化,可以把它置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量),也可以通过调用 pthread_mutex_init 函数进行初始化,如果动态地分配互斥量,那么释放内存前需要调用 pthread_mutex_destroy。

1.初始化/回收互斥锁

初始化:

文件:#include <pthread.h>

函数原型:

动态:int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

静态:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

mutex 是我们要锁住的互斥量,attr 是互斥锁的属性,可用相应的函数修改,要用默认的属性初始化互斥量,只需把 attr 设置为 NULL。

pthread_mutex_init()函数是以动态方式创建互斥锁的,参数attr指定了新建互斥锁的属性。如果参数attr为空,则使用默认的互斥锁属性,默认属性为快速互斥锁 。互斥锁的属性在创建锁的时候指定,在Linux Threads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。若创建成功则返回 0,否则返回错误编号。

回收:

原型:
  #include <pthread.h> 
  int pthread_mutex_destroy(pthread_mutex_t *mutex); //mutex 指向要销毁的互斥锁的指针
  互斥锁销毁函数在执行成功后返回 0,否则返回错误码。

2.对互斥量加减锁

对互斥量进行加锁,需要调用 pthread_mutex_lock,如果互斥量已经上锁,调用线程阻塞直至互斥量解锁。对互斥量解锁,需要调用 pthread_mutex_unlock.如果线程不希望被阻塞,他可以使用 pthread_mutex_trylock 尝试对互斥量进行加锁。如果调用pthread_mutex_trylock 时互斥量处于未锁住状态,那么pthread_mutex_trylock 将锁住互斥量,否则就会失败,不能锁住互斥量,而返回
EBUSY。

函数原型:

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread_mutex_t *mutex);

pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁,即同一时间只 能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一 个线程释放此互斥锁。拥有mutex的线程不能进入休眠或者被中断,如果线程A拥有mutex,B试图获得mutex,B会一直阻塞,此时A

进入休眠或者被中断,当A再次执行时,需要pthread_mutex_lock获得锁,可是mutex并没有被A释放,所以此时就会出现死锁。

我们先来看不加锁的程序:

#inlcude <stdio.h>#include <pthread.h>#inlcude <stdio.h>#include <unistd.h>viid *thread_function(void *arg);int run_now=1; /*用 run_now 代表共享资源*/int main(){int print_count1=0; /*用于控制循环*/prhread_t a_thread;if(pthread_create(&a_thread,NULL,thread_function,NULL)!=0) /*创建一个进程*/{perror(“Thread createion failed”);exit(1);}while(print_count1++<5){if(run_now==1) /*主线程:如果 run_now 为 1 就把它修改为 2*/{printf(“main thread is run\n”);run_now=2;}else{printf(“main thread is sleep\n”);sleep(1);}}pthread_join(a_thread,NULL); /*等待子线程结束*/exit(0);}void *thread_function(void *arg){int print_count2=0;while(print_count2++<5){if(run_now==2) /*子线程:如果 run_now 为 1 就把它修改为 1*/{printf(“function thread is run\n”);run_now=1;}else{printf(“function thread is sleep\n”);sleep(1);}}pthread_exit(NULL);}

运行上面程序的运行结果为:
function thread is sleep
main thread is run
main thread is sleep
main thread is sleep
function thread is run
function thread is sleep
main thread is run
main thread is sleep
function thread is run

function thread is sleep

我们可以看到 main 线程和 function 线程是交替运行的。它们都可以对 run_now进行操作,这样就容易混乱。


接下来看一下加了锁的程序:

#inlcude <stdio.h>#include <pthread.h>#inlcude <stdio.h>viid *thread_function(void *arg);int run_now=1; /*用 run_now 代表共享资源*/pthread_mutex_t work_mutex; /*定义互斥量*/int main(){int res;int print_count1=0;prhread_t a_thread;if(pthread_mutex_init(&work_mutex,NULL)!=0) /*初始化互斥量*/{perror(“Mutex init faied”);exit(1);}if(pthread_create(&a_thread,NULL,thread_function,NULL)!=0) /*创建新线程*/{perror(“Thread createion failed”);exit(1);}if(pthread_mutex_lock(&work_mutex)!=0) /*对互斥量加锁*/{preeor(“Lock failed”);exit(1);}elseprintf(“main lock\n”);while(print_count1++<5){if(run_now==1) /*主线程:如果 run_now 为 1 就把它修改为 2*/{printf(“main thread is run\n”);run_now=2;}else{printf(“main thread is sleep\n”);sleep(1);}}if(pthread_mutex_unlock(&work_mutex)!=0) /*对互斥量解锁*/{preeor(“unlock failed”);exit(1);}elseprintf(“main unlock\n”);pthread_mutex_destroy(&work_mutex); /*收回互斥量资源*/pthread_join(a_thread,NULL); /*等待子线程结束*/exit(0);}void *thread_function(void *arg){int print_count2=0;sleep(1);if(pthread_mutex_lock(&work_mutex)!=0){perror(“Lock failed”);exit(1);}elseprintf(“function lock\n”);while(print_count2++<5){if(run_now==2) /*分进程:如果 run_now 为 1 就把它修改为 1*/{printf(“function thread is run\n”);run_now=1;}else{printf(“function thread is sleep\n”);sleep(1);}}if(pthread_mutex_unlock(&work_mutex)!=0) /*对互斥量解锁*/{perror(“unlock failed”);exit(1);}elseprintf(“function unlock\n”);pthread_exit(NULL);}

下面是运行结果:
main lock
main thread is run
main thread is sleep
main thread is sleep
main thread is sleep
main thread is sleep
main unlock
function lock
function thread is run
function thread is sleep
function thread is sleep
function thread is sleep
function thread is sleep
function unlock
我们从运行结果可以看到,当主线程把互斥量锁住后,子线程就不能对共享资源进行操作了。


3.互斥锁属性

线程和线程的同步对象(互斥量,读写锁,条件变量)都具有属性。在修改属性前都需要对该结构进行初始化。使用后要把该结构回收。我们用 pthread_mutexattr_init 函数对 pthread_mutexattr 结构进行初始化,用pthread_mutexattr_destroy 函数对该结构进行回收。

名称:

pthread_mutexattr_init/pthread_mutexattr_destroy

功能:

初始化/回收pthread_mutexattr_t结构

头文件:

#include <pthread.h>

函数原形:

int pthread_mutexattrattr_init(pthread_mutexattr_t *attr);

int pthread_mutexattrattr_destroy( pthread_mutexattr_t *attr );

参数:

attr    pthread_mutexattr_t结构变量

返回值:

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












pthread_mutexattr_init 将属性对象的值初始化为缺省值。并分配属性对象占用的内存空间。attr 中 pshared 属性表示用这个属性对象创建的互斥锁的作用域,它的取值可以是 PTHREAD_PROCESS_PRIVATE(缺省值,表示由这个属性对象创建的互斥
锁只能在进程内使用)或 PTHREAD_PROCESS_SHARED。
互斥量属性分为共享互斥量属性和类型互斥量属性。两种属性分别由不同的函数得到并由不同的函数进行修改。pthread_mutexattr_getpshared 和pthread_mutexattr_setpshared 函数可以获得和修改共享互斥量属性。
pthread_mutexattr_gettype 和 pthread_mutexattr_settype 函数可以获得和修改类型互斥量属性。下面我们分别介绍:

名称:

pthread_mutexattr_getpshared/pthread_mutexattr_setpshared

功能:

获得/修改共享互斥量属性

头文件:

#include <pthread.h>

函数原形:

int pthread_mutexattrattr_ getpshared ( const pthread_attr_t *restrict attr,int*restrict pshared);

int pthread_mutexattrattr_ setpshared (  const pthread_attr_t *restrict attr,int pshared);

参数:

 

返回值:

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











共享互斥量属性用于规定互斥锁的作用域。互斥锁的域可以是进程内的也可以是进程间的。pthread_mutexattrattr_ getpshared 可以返回属性对象的互斥锁作用域属性。可以是以下值:

PTHREAD_PROCESS_SHARED,
PTHREAD_PROCESS_PRIVATE。

如果互斥锁属性对象的 pshared 属性被置PTHREAD_PROCESS_SHARED。那么由这个属性对象创建的互斥锁将被保存在共享内
存中,可以被多个进程中的线程共享。

如果 pshared 属性被置为PTHREAD_PROCESS_PRIVATE,那么只有和创建这个互斥锁的线程在同一个进程中的线程才能访问这个互斥锁。

名称:

pthread_mutexattr_gettype/pthread_mutexattr_settype

功能:

获得/修改类型互斥量属性

头文件:

#include <pthread.h>

函数原形:

int pthread_mutexattrattr_ getpshared ( const pthread_attr_t *restrict attr,int*restrict pshared);

int pthread_mutexattrattr_ setpshared (  const pthread_attr_t *restrict attr,int pshared);

参数:

 

返回值:

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











pthread_mutexattr_gettype 函数可以获得互斥锁类型属性。
缺省的互斥锁类型属性
是 PTHREAD_MUTEX_DEFAULT。
合法的类型属性值有:
PTHREAD_MUTEX_NORMAL;
PTHREAD_MUTEX_ERRORCHECK;
PTHREAD_MUTEX_RECURSIVE;
PTHREAD_MUTEX_DEFAULT。


类型说明:
PTHREAD_MUTEX_NORMAL
这种类型的互斥锁不会自动检测死锁。如果一个线程试图对一个互斥锁重复锁定,将会引起这个线程的死锁。如果试图解锁一个由别的线程锁定的互斥锁会引发不可预料的结果。如果一个线程试图解锁已经被解锁的互斥锁也会引发不可预料的结果。


PTHREAD_MUTEX_ERRORCHECK
这种类型的互斥锁会自动检测死锁。如果一个线程试图对一个互斥锁重复锁定,将会返回一个错误代码。如果试图解锁一个由别的线程锁定的互斥锁将会返回一个错误代码。如果一个线程试图解锁已经被解锁的互斥锁也将会返回一个错误代码。


PTHREAD_MUTEX_RECURSIVE
如果一个线程对这种类型的互斥锁重复上锁,不会引起死锁,一个线程对这类互斥锁的多次重复上锁必须由这个线程来重复相同数量的解锁,
这样才能解开这个互斥锁,别的线程才能得到这个互斥锁。如果试图解锁一个由别的线程锁定的互斥锁将会返回一个错误代码。如果一个线程试图解锁已经被解锁的互斥锁也将会返回一个错误代码。这种类型的互斥锁只能是进程私有的(作用域属性为PTHREAD_PROCESS_PRIVATE)。


PTHREAD_MUTEX_DEFAULT
这种类型的互斥锁不会自动检测死锁。如果一个线程试图对一个互斥锁重复锁定,将会引起不可预料的结果。如果试图解锁一个由别的线程锁定的互斥锁会引发不可预料的结果。如果一个线程试图解锁已经被解锁的互斥锁也会引发不可预料的结果。POSIX 标准规定,对于某一具体的实现,可以把这种类型的互斥锁定义为其他类型的互斥锁。

4.应用互斥量需要注意的几点
1、互斥量需要时间来加锁和解锁。锁住较少互斥量的程序通常运行得更快。所以,互斥量应该尽量少,够用即可,每个互斥量保护的区域应则尽量大。
2、互斥量的本质是串行执行。如果很多线程需要领繁地加锁同一个互斥量,则线程的大部分时间就会在等待,这对性能是有害的。如果互斥量保护的数据(或代码)包含彼此无关的片段,则可以特大的互斥量分解为几个小的互斥量来提高性能。这样,任意时刻需要小互斥量的线程减少,线程等待时间就会减少。所以,互斥量应该足够多(到有意义的地步),每个互斥量保护的区域则应尽量的少。



0 0
原创粉丝点击