Mutex Variables(互斥量)
Thread 1 | Thread 2 | Balance |
---|---|---|
Read balance: $1000 | $1000 | |
Read balance: $1000 | $1000 | |
Deposit $200 | $1000 | |
Deposit $200 | $1000 | |
Update balance $1000+$200 | $1200 | |
Update balance $1000+$200 | $1200 |
- 创建并初始化互斥量;
- 多个线程试图锁定此互斥量;
- 有且只有一个线程成功的拥有了这个互斥量;
- 拥有互斥量的线程执行了一系列的操作;
- 拥有互斥量的线程解锁互斥量;
- 另一个线程获得此互斥量,并重复上面的过程;
- 最终互斥量被销毁。
创建并销毁互斥量
函数:
pthread_mutex_init (mutex,attr)
pthread_mutex_destroy (mutex)
pthread_mutexattr_init (attr)
pthread_mutexattr_destroy (attr)
用法:
- 静态初始化,例如:pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
- 动态初始化,使用pthread_mutex_init()过程,此函数可以设置互斥量属性对象attr;
注意,互斥量初始状态是未被锁定的(unlocked)。
- Protocol:为互斥量指定用来防止优先级倒置的规则的;
- Prioceiling:为互斥量指定优先级上限的;
- Process-shared:为互斥量指定进程共享的。
注意,不是所有的实现都要提供这三个可选的互斥量属性的。
锁定和解锁互斥量
函数:
pthread_mutex_lock (mutex) pthread_mutex_trylock (mutex) pthread_mutex_unlock (mutex) |
用法:
- 如果互斥量已经被解锁;
- 如果互斥量被另一个线程拥有。
案例分析
/* ****************************************************************************
* 描述:
* 这个例子阐明了互斥变量在线程编程中的用法
* main数据域定义一个全局的可访问结构体,所有线程均可访问,
* 每个线程都为此数据的一部分做运算工作,主线程等待所有线程完成运算,
* 并最后打印出计算结果。
***************************************************************************** */
#include < pthread.h >
#include < stdio.h >
#include < stdlib.h >
/* 下面的结构体包含了dotprod函数要处理的所需要的信息,
* 访问输入数据并把输出写入结构体
* */
typedef struct {
double * a;
double * b;
double sum; // ab的和
int veclen; // 结构体个数
} DOTDATA;
/* 全局变量和一个互斥量 */
#define NUMTHRDS 4
#define VECLEN 100
DOTDATA dotstr;
pthread_t callThd[NUMTHRDS];
pthread_mutex_t mutexsum;
/* dotprod函数在线程创建时被激活,从DOTDATA结构体内取出数据,之后把运算结果又写入到结构体中。
* 这种处理方法的好处很明显是多线程编程:线程创建时,我们给被激活的函数传递了一个参数——参数值是线程的序号。
* 函数需要的其它信息是来自全局的结构体。
* */
void * dotprod( void * arg) {
int i, start, end, len;
long offset;
double mysum, * x, * y;
offset = ( long ) arg;
len = dotstr.veclen;
start = offset * len;
end = start + len;
x = dotstr.a;
y = dotstr.b;
// 计算
mysum = 0 ;
for (i = start; i < end; i ++ ) {
mysum += (x[i] * y[i]);
}
/* 锁定互斥量,更改共享结构的值,解锁 */
pthread_mutex_lock( & mutexsum);
dotstr.sum += mysum;
printf( " Thread%ld: mysum=%f,dotstr.sum=%f\n " ,offset,mysum,dotstr.sum);
pthread_mutex_unlock( & mutexsum);
pthread_exit(( void * ) 0 );
}
/* 主程序创建线程,线程计算数据,然后打印出结果。在创建线程前,创建输入数据。
* 因为所有线程更改同一个结构体,所以我们需要一个互斥量。
* 主线程需要等待所有线程完成,它等待这些线程中的一个,我们为线程赋予属性,允许主线程连接(join)其它被
* 创建的线程。注意,当不再需要它们是要记得释放它们。
* */
int main( int argc, char * argv[]) {
long i;
double * a, * b;
void * status;
pthread_attr_t attr;
/* 初始化变量 */
a = ( double * ) malloc(NUMTHRDS * VECLEN * sizeof ( double ));
b = ( double * ) malloc(NUMTHRDS * VECLEN * sizeof ( double ));
for (i = 0 ; i < VECLEN * NUMTHRDS; i ++ ) {
a[i] = 1 ;
b[i] = a[i];
}
dotstr.veclen = VECLEN;
dotstr.a = a;
dotstr.b = b;
dotstr.sum = 0 ;
pthread_mutex_init( & mutexsum, NULL);
pthread_attr_init( & attr);
pthread_attr_setdetachstate( & attr, PTHREAD_CREATE_JOINABLE);
for (i = 0 ; i < NUMTHRDS; i ++ ) {
/* 创建线程,激活dotpod函数,传入参数线程序号 */
printf( " i=%d\n " ,i);
pthread_create( & callThd[i], & attr, dotprod, ( void * ) i);
}
pthread_attr_destroy( & attr);
/* 等待其它线程 */
for (i = 0 ; i < NUMTHRDS; i ++ ) {
pthread_join(callThd[i], & status);
}
/* 线程join后,释放内存,销毁互斥量 */
printf( " Sum = %f \n " , dotstr.sum);
free(a);
free(b);
pthread_mutex_destroy( & mutexsum);
pthread_exit(NULL);
}
# Pthreads
i = 0
Thread0: mysum = 100.000000 ,dotstr.sum = 100.000000
i = 1
Thread1: mysum = 100.000000 ,dotstr.sum = 200.000000
i = 2
Thread2: mysum = 100.000000 ,dotstr.sum = 300.000000
i = 3
Thread3: mysum = 100.000000 ,dotstr.sum = 400.000000
Sum = 400.000000
指针和内存的图像化
主程序申请了两块内存,各分为四块,上图标明了各个指针的指向,程序共创建四个线程,把两块4*100的内存区域分成四块,做连加运算得到mysum,之后再把所有的运算结果加起来赋值于dostr.sum,由于它是多线程共享的全局变量这里得利用互斥变量排队做加法,才能保证该值结果的正常。