POSIX 线程编程指南(四)

 

Mutex 变量

概述

  • Mutex"mutual exclusion"的缩写,Mutex变量是线程同步和保护共享数据的主要方式。
  • 一个Mutex变量就像一把“锁”,用来保护共享资源,在Pthreads中,Mutex的基本概念是:在任何时候,只有一个线程能够获得Mutex,因此,尽管几个线程想获取一个Mutex,但是只有一个线程能够成功。其他线程需要等待,直到获取Mutex的线程放弃Mutex。线程必须轮流访问需要保护的数据。
  • Mutex可以用来阻止竞争,下面是一个银行的竞争事务示例。

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

  • 在上面的例子中,当一个线程使用共享数据时,mutex应该加锁"Balance"
  • 线程经常利用mutex来加锁需要更新的全局变量,这也是几个线程需要同时更新全局变量时使用的的安全方法。 这样能保证在多线程环境下的全局变量的更新就如在单线程环境中一样。此全局变量的更新属于"critical section"
  • 使用mutex的典型步骤如下::
    • 创建和初始化mutex 
    • 多个线程试图获取mutex 
    • 仅仅只有一个线程能够获取mutex 并拥有它
    • 拥有此mutex 的线程执行一些事情
    • 然后这个线程释放mutex 
    • 另外一个线程获取此mutex ,然后重复上面的步骤。
    • 最后mutex 被销毁
  • 当几个线程竞争一个mutex时,没有得到mutex的线程将被阻塞,利用"trylock"变量可以进行非阻塞调用。
  • 当保护共享资源时,开发者有义务确认每个线程都使用了mutex。比如,如果4个线程需要更新同一数据,只有一个线程使用了mutex,那么这个数据仍然会被损害。

 

Mutex 变量

创建和销毁 Mutex

接口:

pthread_mutex_init (mutex,attr)

pthread_mutex_destroy (mutex)

pthread_mutexattr_init (attr)

pthread_mutexattr_destroy (attr)

用法:

  • mutex必须用pthread_mutex_t数据类型声明,在使用前必须已经初始化,这里有2种方式初始化mutex变量:
    1. 静态声明:
      pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
    2. 动态声明,使用pthread_mutex_init(). 利用这个方法可以设置 mutex object的属性: attr

Mutex初始时是unlocked(未加锁的)的。

  • attr object是用来设置mutex对象属性的,如果使用它,那么它一定是 pthread_mutexattr_t 类型 (可以设置NULL作为缺省的)Pthreads标准定义了三种可选的mutex属性:
    • Protocol: 利用特定的协议来防止mutex优先级倒置。
    • Prioceiling: 特定的mutex优先级限制。
    • Process-shared: 特定的进程共享mutex

注意并不是所有的应用提供所有这三种mutex属性。

  • pthread_mutexattr_init()pthread_mutexattr_destroy()分别被用来创建和销毁mutex属性对象。
  • mutex对象不再需要时,pthread_mutex_destroy()被用来释放mutex对象。

 

Mutex变量

加锁和解锁Mutexes

Routines:

pthread_mutex_lock (mutex)

pthread_mutex_trylock (mutex)

pthread_mutex_unlock (mutex)

用法:

·         pthread_mutex_lock() 被线程用来加锁一个特定的mutex,如果此mutex已经被其他线程加锁,这个调用会阻塞线程直到mutex被解锁。

  • pthread_mutex_trylock()将尝试加锁mutex,如果mutex已经被加锁了,这个调用会立即返回,并返回一个"busy"错误码。利用此调用可以防止在优先级倒置所出现的死锁。
  • 如果拥有mutex的线程调用pthread_mutex_unlock()将解锁mutex,当这个线程使用完了保护的资源后,如果其他线程需要操作这个资源,拥有mutex的线程就应该调用pthread_mutex_unlock()去解锁mutex。在下面的情况中,将返回一个错误:
    • 如果mutex已经解锁了
    • 如果mutex是被另外的线程加锁的
  • metux没有什么不可思议的("magical",实际上,它在线程间是君子协定("gentlemen's agreement"),它们由程序开发者来保证所有线程能够正确加锁/解锁mutex,下面示例了一个逻辑错误:

·                    Thread 1     Thread 2     Thread 3

·                    Lock         Lock        

·                    A = 2        A = A+1      A = A*B

·                    Unlock       Unlock   

Example: 使用 Mutexes

Example Code - Using Mutexes

This example program illustrates the use of mutex variables in a threads program that performs a dot product. The main data is made available to all threads through a globally accessible structure. Each thread works on a different part of the data. The main thread waits for all the threads to complete their computations, and then it prints the resulting sum.


#include <pthread.h>

#include <stdio.h>

#include <malloc.h>

 

/*  

The following structure contains the necessary information 

to allow the function "dotprod" to access its input data and

place its output into the structure. 

*/

 

typedef struct

 {

   double      *a;

   double      *b;

   double     sum;

   int     veclen;

 } DOTDATA;

 

/* Define globally accessible variables and a mutex */

 

#define NUMTHRDS 4

#define VECLEN 100

   DOTDATA dotstr;

   pthread_t callThd[NUMTHRDS];

   pthread_mutex_t mutexsum;

 

/*

The function dotprod is activated when the thread is created.

All input to this routine is obtained from a structure

of type DOTDATA and all output from this function is written into

this structure. The benefit of this approach is apparent for the

multi-threaded program: when a thread is created we pass a single

argument to the activated function - typically this argument

is a thread number. All  the other information required by the

function is accessed from the globally accessible structure.

*/

 

void *dotprod(void *arg)

{

 

   /* Define and use local variables for convenience */

 

   int i, start, end, offset, len ;

   double mysum, *x, *y;

   offset = (int)arg;

    

   len = dotstr.veclen;

   start = offset*len;

   end   = start + len;

   x = dotstr.a;

   y = dotstr.b;

 

   /*

   Perform the dot product and assign result

   to the appropriate variable in the structure.

   */

 

   mysum = 0;

   for (i=start; i<end ; i++)

    {

      mysum += (x[i] * y[i]);

    }

 

   /*

   Lock a mutex prior to updating the value in the shared

   structure, and unlock it upon updating.

   */

   pthread_mutex_lock (&mutexsum);

   dotstr.sum += mysum;

   pthread_mutex_unlock (&mutexsum);

 

   pthread_exit((void*) 0);

}

 

/*

The main program creates threads which do all the work and then

print out result upon completion. Before creating the threads,

the input data is created. Since all threads update a shared structure,

we need a mutex for mutual exclusion. The main thread needs to wait for

all threads to complete, it waits for each one of the threads. We specify

a thread attribute value that allow the main thread to join with the

threads it creates. Note also that we free up handles when they are

no longer needed.

*/

 

int main (int argc, char *argv[])

{

   int i;

   double *a, *b;

   void *status;

   pthread_attr_t attr;

 

   /* Assign storage and initialize values */

   a = (double*) malloc (NUMTHRDS*VECLEN*sizeof(double));

   b = (double*) malloc (NUMTHRDS*VECLEN*sizeof(double));

 

   for (i=0; i<VECLEN*NUMTHRDS; i++)

    {

     a[i]=1.0;

     b[i]=a[i];

    }

 

   dotstr.veclen = VECLEN;

   dotstr.a = a;

   dotstr.b = b;

   dotstr.sum=0;

 

   pthread_mutex_init(&mutexsum, NULL);

        

   /* Create threads to perform the dotproduct  */

   pthread_attr_init(&attr);

   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

 

        for(i=0; i<NUMTHRDS; i++)

        {

        /*

        Each thread works on a different set of data.

        The offset is specified by 'i'. The size of

        the data for each thread is indicated by VECLEN.

        */

        pthread_create( &callThd[i], &attr, dotprod, (void *)i);

        }

 

        pthread_attr_destroy(&attr);

 

        /* Wait on the other threads */

        for(i=0; i<NUMTHRDS; i++)

        {

          pthread_join( callThd[i], &status);

        }

 

   /* After joining, print out the results and cleanup */

   printf ("Sum =  %f /n", dotstr.sum);

   free (a);

   free (b);

   pthread_mutex_destroy(&mutexsum);

   pthread_exit(NULL);

}  

 

下面是示例的全部 

Serial versionPthreads version

/******************************************************************************
* FILE: dotprod_serial.c
* DESCRIPTION:
*   This is a simple serial program which computes the dot product of two 
*   vectors.  The threaded version can is dotprod_mutex.c.
* SOURCE: Vijay Sonnad, IBM
* LAST REVISED: 04/05/05 Blaise Barney
******************************************************************************/
#include 
< stdio .h >
#include 
< stdlib .h >

/*   
The following structure contains the necessary information  
to allow the function "dotprod" to access its input data and 
place its output so that it can be accessed later. 
*/

typedef struct 
{
  double      *a;
  double      *b;
  double     sum; 
  int    veclen; 
} DOTDATA;

#define VECLEN 100
  DOTDATA dotstr; 

/*
We will use a function (dotprod) to perform the scalar product. 
All input to this routine is obtained through a structure of 
type DOTDATA and all output from this function is written into
this same structure.  While this is unnecessarily restrictive 
for a sequential program, it will turn out to be useful when
we modify the program to compute in parallel.
*/

void *dotprod(void)
{

/* Define and use local variables for convenience */

   int start, end, i; 
   double mysum, *x, *y;

   start=0;
   end = dotstr.veclen;
   x = dotstr.a;
   y = dotstr.b;

/*
Perform the dot product and assign result
to the appropriate variable in the structure. 
*/

   mysum = 0;
   for (i=start; i
< end  ; i++) 
    {
      mysum +
= (x[i]  * y[i]);
    }
   dotstr.sum 
= mysum;

   
return ;
}

/*
The main program initializes data and calls the dotprd() function.
Finally, it prints the result.
*/

int main (int argc, char *argv[])
{
int i,len;
double *a, *b;
   
/* Assign storage and initialize values */
len 
= VECLEN;
= (double*)  malloc (len*sizeof(double));
= (double*)  malloc (len*sizeof(double));
  
for (i
=0;  i<len; i++) {
  a[i]
=1;
  
b[i] =a[i];
  
}

dotstr.veclen 
= len; 
dotstr.a  = a; 
dotstr.b  = b; 
dotstr.sum =0;

/* Perform the  dotproduct */
dotprod ();

/* Print result and release storage */ 
printf ("Sum 
=  %f  ", dotstr.sum);
free (a);
free (b);
}

 

 

/*****************************************************************************
* FILE: dotprod_mutex.c
* DESCRIPTION:
*   This example program illustrates the use of mutex variables 
*   in a threads program. This version was obtained by modifying the
*   serial version of the program (dotprod_serial.c) which performs a 
*   dot product. The main data is made available to all threads through 
*   a globally accessible  structure. Each thread works on a different 
*   part of the data. The main thread waits for all the threads to complete 
*   their computations, and then it prints the resulting sum.
* SOURCE: Vijay Sonnad, IBM
* LAST REVISED: 04/06/05 Blaise Barney
******************************************************************************/
#include 
< pthread .h >
#include 
< stdio .h >
#include 
< stdlib .h >

/*   
The following structure contains the necessary information  
to allow the function "dotprod" to access its input data and 
place its output into the structure.  This structure is 
unchanged from the sequential version.
*/

typedef struct 
 {
   double      *a;
   double      *b;
   double     sum; 
   int     veclen; 
 } DOTDATA;

/* Define globally accessible variables and a mutex */

#define NUMTHRDS 4
#define VECLEN 100
   DOTDATA dotstr; 
   pthread_t callThd[NUMTHRDS];
   pthread_mutex_t mutexsum;

/*
The function dotprod is activated when the thread is created.
As before, all input to this routine is obtained from a structure 
of type DOTDATA and all output from this function is written into
this structure. The benefit of this approach is apparent for the 
multi-threaded program: when a thread is created we pass a single
argument to the activated function - typically this argument
is a thread number. All  the other information required by the 
function is accessed from the globally accessible structure. 
*/

void *dotprod(void *arg)
{

/* Define and use local variables for convenience */

   int i, start, end, offset, len ;
   double mysum, *x, *y;
   offset = (int)arg;
     
   len = dotstr.veclen;
   start = offset*len;
   end   = start + len;
   x = dotstr.a;
   y = dotstr.b;

/*
Perform the dot product and assign result
to the appropriate variable in the structure. 
*/

   mysum = 0;
   for (i=start; i
< end  ; i++) 
    {
      mysum +
= (x[i]  * y[i]);
    }

/*
Lock a mutex prior to updating the value in the shared
structure, and unlock it upon updating.
*/
   pthread_mutex_lock (&mutexsum);
   dotstr.sum +
= mysum;
   
pthread_mutex_unlock (&mutexsum);

   pthread_exit((void*) 0);
}

/* 
The main program creates threads which do all the work and then 
print out result upon completion. Before creating the threads,
The input data is created. Since all threads update a shared structure, we
need a mutex for mutual exclusion. The main thread needs to wait for
all threads to complete, it waits for each one of the threads. We specify
a thread attribute value that allow the main thread to join with the
threads it creates. Note also that we free up handles  when they are
no longer needed.
*/

int main (int argc, char *argv[])
{
int i;
double *a, *b;
void *status;
pthread_attr_t attr;

/* Assign storage and initialize values */

= (double*)  malloc (NUMTHRDS*VECLEN*sizeof(double));
= (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);
         
/* Create threads to perform the dotproduct  */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

for(i
=0;i<NUMTHRDS;i++)
  
{
  /* Each thread works on a different set of data.
   * The offset is specified by 'i'. The size of
   * the data for each thread is indicated by VECLEN.
   */
   pthread_create( &callThd[i], &attr, dotprod, (void *)i); 
   }

pthread_attr_destroy(&attr);
/* Wait on the other threads */

for(i
=0;i<NUMTHRDS;i++)  {
  pthread_join( callThd[i], &status);
  }
/* After joining, print out the results and cleanup */

printf ("Sum 
=  %f  ", dotstr.sum);
free (a);
free (b);
pthread_mutex_destroy(&mutexsum);
pthread_exit(NULL);
}   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值