pThread线程(二) 线程同步--互斥量/锁

  互斥量(Mutex)是“mutual exclusion”的缩写。互斥量是实现线程同步,和保护同时写共享数据的主要方法。
  互斥量对共享数据的保护就像一把锁。在Pthreads中,任何时候仅有一个线程可以锁定互斥量,因此,当多个线程尝试去锁定该互斥量时仅有一个会成功。直到锁定互斥量的线程解锁互斥量后,其他线程才可以去锁定互斥量。线程必须轮着访问受保护数据。
  一个拥有互斥量的线程经常用于更新全局变量。确保了多个线程更新同样的变量以安全的方式运行,最终的结果和一个线程处理的结果是相同的。这个更新的变量属于一个“临界区(critical section)”。

  使用互斥量的典型顺序如下:

  • 创建和初始一个互斥量
  • 多个线程尝试去锁定该互斥量
  • 仅有一个线程可以成功锁定改互斥量
  • 锁定成功的线程做一些处理
  • 线程解锁该互斥量
  • 另外一个线程获得互斥量,重复上述过程
  • 最后销毁互斥量

  当多个线程竞争同一个互斥量时,失败的线程会阻塞在lock调用处。可以用“trylock”替换“lock”,则失败时不会阻塞。当保护共享数据时,程序员有责任去确认是否需要使用互斥量。如,若四个线程会更新同样的数据,但仅有一个线程用了互斥量,则数据可能会损坏。

创建和销毁互斥量:

pthread_mutex_init (mutex,attr)
pthread_mutex_destroy (mutex)
pthread_mutexattr_init (attr)
pthread_mutexattr_destroy (attr) 

用法

互斥量必须用类型pthread_mutex_t类型声明,在使用前必须初始化,这里有两种方法可以初始化互斥量:
声明时静态地,如: pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
动态地用pthread_mutex_init()函数,这种方法允许设定互斥量的属性对象attr。
互斥量初始化后是解锁的。
attr对象用于设置互斥量对象的属性,使用时必须声明为pthread_mutext_attr_t类型,默认值可以是NULL。Pthreads标准定义了三种可选的互斥量属性:

  • 协议(Protocol): 指定了协议用于阻止互斥量的优先级改变
  • 优先级上限(Prioceiling):指定互斥量的优先级上限
  • 进程共享(Process-shared):指定进程共享互斥量

注意所有实现都提供了这三个可先的互斥量属性。
pthread_mutexattr_init()和pthread_mutexattr_destroy()函数分别用于创建和销毁互斥量属性对象。
pthread_mutex_destroy()应该用于释放不需要再使用的互斥量对象。

锁定和解锁互斥量:

函数

pthread_mutex_lock (mutex)
pthread_mutex_trylock (mutex)
pthread_mutex_unlock (mutex)  

用法

线程用pthread_mutex_lock()函数去锁定指定的mutex变量,若该mutex已经被另外一个线程锁定了,该调用将会阻塞线程直到mutex被解锁。
pthread_mutex_trylock() will attempt to lock a mutex. However, if the mutex is already locked, the routine will return immediately with a "busy" error code. This routine may be useful in pthread_mutex_trylock().

  尝试着去锁定一个互斥量,然而,若互斥量已被锁定,程序会立刻返回并返回一个忙错误值。该函数在优先级改变情况下阻止死锁是非常有用的。线程可以用pthread_mutex_unlock()解锁自己占用的互斥量。在一个线程完成对保护数据的使用,而其它线程要获得互斥量在保护数据上工作时,可以调用该函数。若有一下情形则会发生错误:

  • 互斥量已经被解锁
  • 互斥量被另一个线程占用

互斥量并没有多么“神奇”的,实际上,它们就是参与的线程的“君子约定”。写代码时要确信正确地锁定,解锁互斥量。
Q:有多个线程等待同一个锁定的互斥量,当互斥量被解锁后,那个线程会第一个锁定互斥量?
A:除非线程使用了优先级调度机制,否则,线程会被系统调度器去分配,那个线程会第一个锁定互斥量是随机的。

用例: 

 1 #include<stdlib.h>
 2 #include<stdio.h>
 3 #include<unistd.h>
 4 #include<pthread.h>
 5
 6 typedef struct ct_sum
 7 {
 8     int sum;
 9     pthread_mutex_t lock;
10 }ct_sum;
11
12 void * add1(void *cnt)
13 {
14     pthread_mutex_lock(&(((ct_sum*)cnt)->lock));
15     for(int i=0; i < 50; i++)
16     {
17         (*(ct_sum*)cnt).sum += i;
18     }
19     pthread_mutex_unlock(&(((ct_sum*)cnt)->lock));
20     pthread_exit(NULL);
21     return 0;
22 }
23 void * add2(void *cnt)
24 {
25     pthread_mutex_lock(&(((ct_sum*)cnt)->lock));
26     for(int i=50; i<101; i++)
27     {
28          (*(ct_sum*)cnt).sum += i;
29     }
30     pthread_mutex_unlock(&(((ct_sum*)cnt)->lock));
31     pthread_exit(NULL);
32     return 0;
33 }
34
35 int main(void)
36 {
37     pthread_t ptid1, ptid2;
38     ct_sum cnt;
39     pthread_mutex_init(&(cnt.lock), NULL);
40     cnt.sum=0;
41
42     pthread_create(&ptid1, NULL, add1, &cnt);
43     pthread_create(&ptid2, NULL, add2, &cnt);
44
45     pthread_join(ptid1,NULL);
46     pthread_join(ptid2,NULL);
47
48     printf("sum %d\n", cnt.sum);
49     pthread_mutex_destroy(&(cnt.lock));
50
51     return 0;
52 }  
时间: 2024-10-14 12:11:01

pThread线程(二) 线程同步--互斥量/锁的相关文章

Linux程序设计学习笔记----多线程编程线程同步机制之互斥量(锁)与读写锁

互斥锁通信机制 基本原理 互斥锁以排他方式防止共享数据被并发访问,互斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个互斥锁逻辑上绑定之后,对该资源的访问操作如下: (1)在访问该资源之前需要首先申请互斥锁,如果锁处于开状态,则申请得到锁并立即上锁(关),防止其他进程访问资源,如果锁处于关,则默认阻塞等待. (2)只有锁定该互斥锁的进程才能释放该互斥锁. 互斥量类型声明为pthread_mutex_t数据类型,在<bits/pthreadtypes.h>中有具体的定义. 互斥量

线程同步——互斥量

同步的概念 多个线程可以共享内存空间,在程序中不可避免的需要多个线程协作完成某个功能.那么这些线程就可能会使用某个公共的资源.比如说全局变量,某个文件等等.为了不产生冲突,冲突会产生在多个线程的写操作之间,而读操作则很安全.这就需要多个线程之间的同步. 互斥量的原理 互斥量 实现同步的机制很好理解.可以将互斥量想象为锁.只有当一个线程获得该锁时才有权限对共享资源的操作.从而可以理解为该线程对共享资源上了一把锁,其他线程无权操作.在此线程操作完成之后,需要解锁以便其他线程可以获得该锁.可以想的出来

linux系统编程:线程同步-互斥量(mutex)

线程同步-互斥量(mutex) 线程同步 多个线程同时访问共享数据时可能会冲突,于是需要实现线程同步. 一个线程冲突的示例 #include <stdio.h> #include <unistd.h> #include <pthread.h> #define Loop 1000000 //全局资然 int counter = 0; void *fun(void *argv) { int i; for (i = 0; i < Loop; i++) { counter

(转)经典线程同步 互斥量Mutex

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

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

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

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

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

线程同步---互斥量mutex

1. 问题引入:开两个线程同时对一个全局变量10万次做自加,结果会如何? #include <stdio.h> #include <string.h> #include <pthread.h> unsigned int g_cn = 0; void* thread_proc (void* arg) { unsigned int i; for (i = 0; i < 100000; i++) ++g_cn; return NULL; } int main (void

C#中的线程(二) 线程同步基础

1.同步要领 下面的表格列展了.NET对协调或同步线程动作的可用的工具:                       简易阻止方法 构成 目的 Sleep 阻止给定的时间周期 Join 等待另一个线程完成                       锁系统 构成 目的 跨进程? 速度 lock 确保只有一个线程访问某个资源或某段代码. 否 快 Mutex 确保只有一个线程访问某个资源或某段代码.可被用于防止一个程序的多个实例同时运行. 是 中等 Semaphore 确保不超过指定数目的线程访问某

Android(线程二) 线程池详解

我们在ListView中需要下载资源时,赞不考虑缓存机制,那么每一个Item可能都需要开启一个线程去下载资源(如果没有线程池),如果Item很多,那么我们可能就会无限制的一直创建新的线程去执行下载任务,最终结果可能导致,应用卡顿.手机反应迟钝!最坏的结果是,用户直接卸载掉该App.所以,我们在实际开发中需要考虑多线程,多线程就离不开线程池.如果你对线程还不了解,可以看看这篇文章,Android(线程一) 线程. 使用线程池的优点: (1).重用线程,避免线程的创建和销毁带来的性能开销: (2).