代码改变世界

Mutex linux线程锁

2015-02-03 11:51  sylar_liang  阅读(839)  评论(0编辑  收藏  举报

1.互斥量:Mutex

  多线程同一时间在同一块内存区域操作数据,防止数据的不一致. 挣用或者竞争情况 通常发生在多线程,执行的操作在相同的内存区域,比如修改同一个状态变量。

锁主要是锁住共享资源, 对于多线程访问的全局变量,需要添加锁。

 

a.用于互斥访问
b.类型:pthread_mutex_t,必须被初始化为PTHREAD_MUTEX_INITIALIZER(用于静态分配的mutex,等价于 pthread_mutex_init(…, NULL))或者调用pthread_mutex_init。Mutex也应该用pthread_mutex_destroy来销毁。
 
有两种方法创建互斥锁,静态方式和动态方式:

1)POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

 
2)动态方式是采用pthread_mutex_init()函数来初始化互斥锁
#include <pthread.h>
1) 初始化互斥锁
int pthread_mutex_init(
       pthread_mutex_t *mutex,
       const pthread_mutexattr_t *mutexattr); // 
其中mutexattr用于指定互斥锁属性

互斥锁属性
  互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。有四个值可供选择:
  * PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
  * PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
  * PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
  * PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

 
函数 pthread_mutex_init用来生成一个互斥锁,mutexattr 传入 NULL 表明使用默认属性;如果需要声明特定属性的互斥锁,须调用函数 pthread_mutexattr_init.
 
attr中pshared属性表示用这个属性对象创建的互斥锁的作用域,它的取值可以是PTHREAD_PROCESS_PRIVATE(缺省值,表示由这个属性对象创建的互斥锁只能在进程内使用)或PTHREAD_PROCESS_SHARED。
 
互斥量属性分为共享互斥量属性和类型互斥量属性。两种属性分别由不同的函数得到并由不同的函数进行修改。
 
>>
pthread_mutexattr_getpshared:获取互斥锁范围 
pthread_mutexattr_setpshared:设置互斥锁范围 
函数可以获得和修改共享互斥量属性。共享互斥量属性用于规定互斥锁的作用域。互斥锁的域可以是进程内的也可以是进程间的。
pthread_mutexattrattr_ getpshared可以返回属性对象的互斥锁作用域属性。可以是以下值:
PTHREAD_PROCESS_SHARED,
PTHREAD_PROCESS_PRIVATE。
如果互斥锁属性对象的pshared属性被置PTHREAD_PROCESS_SHARED。那么由这个属性对象创建的互斥锁将被保存在共享内存中,可以被多个进程中的线程共享。
如果pshared属性被置为PTHREAD_PROCESS_PRIVATE,那么只有和创建这个互斥锁的线程在同一个进程中的线程才能访问这个互斥锁。
 
>>pthread_mutexattr_gettype:获取互斥锁的类型属性 
pthread_mutexattr_settype:设置互斥锁的类型属性 
 
2) 取消对互斥锁的初始化
int pthread_mutex_destroy(pthread_mutex_t *mutex);
 
3) 加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
 
4) 测试加锁,与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。
int pthread_mutex_trylock(pthread_mutex_t *mutex);
 
5) 解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
 
 
2.Conditional Variable:条件 cond
a.条件必须被Mutex保护起来
b.类型为:pthread_cond_t,必须被初始化为PTHREAD_COND_INITIALIZER(用于静态分配的条件,等价于pthread_cond_init(…, NULL))或者调用pthread_cond_init
静态的初始化条件变量:pthread_cond_t my_condition = PTHREAD_COND_INITIALIZER;
 
#i nclude <pthread.h>
1) 初始化一个条件变量
int pthread_cond_init(
       pthread_cond_t *restrict cond,
       const pthread_condxattr_t *restrict attr)
 
2) 释放一个条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
 
3) 使线程阻塞在一个条件变量上。用于等待条件发生
int pthread_cond_wait(
       pthread_cond_t *restrict cond,
       pthread_mutex_t *restrict mutex);
 
4) 与3)类似,只是当等待超时的时候返回一个错误值ETIMEDOUT
int pthread_cond_timedwait(
       pthread_cond_t *restrict cond,
       pthread_mutex_t *restrict mutex,
       const struct timespec *restrict timeout);
 
5,6用于通知线程条件被满足
5) 条件改变,发送信号,唤醒单个线程
int pthread_cond_signal(pthread_cond_t *cond);
 
6) 条件满足,则唤醒全部线程
int pthread_cond_broadcast(pthread_cond_t *cond);
 
3.信号量 sem
头文件:#include <semaphore.h>

主要用到的信号量函数有:
sem_init:初始化信号量sem_t,初始化的时候可以指定信号量的初始值,以及是否可以在多进程间共享。
sem_wait:一直阻塞等待直到信号量>0。
sem_timedwait:阻塞等待若干时间直到信号量>0。
sem_post:使信号量加1。
sem_destroy:释放信号量。和sem_init对应。

// 互斥锁类 TLock
#ifndef _TLOCK_H
#define _TLOCK_H

#pragma once

#include <pthread.h>

class TLock
{
    friend class TCond; // 互斥量与条件一起用
public:
    TLock();
    virtual ~TLock(); // 可能作为基类,所以析构函数定义为virtual
    
    void Lock() // 上锁
    {
        pthread_mutex_lock(&m_mutex);    
    }
    
    void Unlock() // 解锁
    {
        pthread_mutex_unlock(&m_mutex);
    }
    
    inline bool Trylock() // 尝试上锁
    {
        return (pthread_mutex_trylock(&m_mutex) == 0);    
    }
    
protected:
    pthread_mutex_t m_mutex; // 互斥锁变量    
};

class TCond
{
public:
    TCond()
    {
        pthread_cond_init(&m_cond, NULL); // 初始化一个条件变量
    }
    
    ~TCond()
    {
        pthread_cond_destroy(&m_cond); // 释放一个条件变量
    }
     
    void Signal()
    {
        pthread_cond_signal(&m_cond); // 条件改变,发送信号,唤醒单个线程
    }
    
    void Boardcast()
    {
        pthread_cond_boardcast(&m_cond); // 条件改变,唤醒全部线程
    }
    
    void Wait(TLock *plock) // 传入TLock 的 mutex
    {
        pthread_cond_wait(&m_cond, &plock->m_mutex); // 使线程阻塞在一个条件变量上,用于等待条件发生
    }
    
    void Wait(TLock *plock, int seconds)
    {
        timespec tv;
        tv.tv_nsec = 0;
        tv.tv_sec = seconds;
        if(pthread_cond_timedwait(&m_cond, &plock->m_mutex, &tv) == 0) // 功能同上,只是等待超时的时候返回一个错误值ETIMEDOUT
            return true;
        else
            return false;
    }
    
private:
    pthread_cond_t m_cond;       
};

// TGuard 直接传一个TLock进来,然后定义一个临时变量,即可实现上锁.
class TGuard
{
public:
    TGuard(TLock &lock)
        : m_lock(lock)
    {
        m_lock.Lock();
    }
    
    ~TGuard()
    {
        m_lock.Unlock();
    }
    
protected:
    TLock& m_lock; // 这里是引用
};

#endif
// TLock.cpp
#include "TLock.h"

static bool attr_initalize = false;
static pthread_mutexattr_t attr;

TLock::TLock()
{
    attr_initalize = false;
    if(!attr_initalize)
    {
        pthread_mutexattr_init(&attr); // 如果需要声明特定属性的互斥锁,须调用函数 pthread_mutexattr_init.
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); // pthread_mutexattr_settype 用来设置互斥锁属性 
                                                                      // PTHREAD_MUTEX_RECURSIVE_NP 嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
        attr_initalize = true;
    }    

    //int pthread_mutex_init(
  //     pthread_mutex_t *mutex,
  //     const pthread_mutexattr_t *mutexattr);
    //其中mutexattr用于指定互斥锁属性

    pthread_mutex_init(&m_mutex, &attr);
}

TLock::~TLock() 
{ 
    pthread_mutex_destroy(&m_mutex); 
}

例子1: mutex的使用

// 使用实例:main.cpp

#include <stdio.h>
#include <stdlib.h>

#include <iostream>
using namespace std;

#include "TLock.h"

void *fnThread1(void *param)
{
    TLock *plock = (TLock *)param;    
    plock->Lock();

    printf("thread1 Lock.\n");
    for(int i=0; i<10; ++i)
    {    
        cout<<"1"<<endl;        
        sleep(1);
    }
    plock->Unlock();
    printf("fnThread1 UnLock.\n");
        
    return NULL;    
}

void *fnThread2(void *param)
{
   usleep(10); // 放弃CPU调度,目的让线程1先运行
    TLock *plock = (TLock *)param;    
    plock->Lock();
    
    printf("fnThread2 Lock.\n");
    for(int i=0; i<10; ++i)
    {
        cout<<"2"<<endl;    
        sleep(1);
    }
    plock->Unlock();
    printf("fnThread2 UnLock.\n");
    
    return NULL;    
}

int main(int argc, char ** argv)
{        
    pthread_t thread1, thread2;
    
    TLock mylock;
    
    pthread_create(&thread1, NULL, fnThread1, (void *)&mylock);
    pthread_create(&thread2, NULL, fnThread2, (void *)&mylock);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    printf("main exit.\n");
    getchar();
    
    return 0;    
}

例子2: mutex, sem 编写的一个水池进水/出水 的例子

// 参考网址
//http://www.cnblogs.com/cs-jin-note/archive/2012/10/30/2746468.html

// 三个线程 
// 线程0 每次+5 in信号量减5 sem_wait
// 线程1 每次+4 in信号量减4 sem_wait
// 线程2 每次-3 out信号量减3 sem_post
// 如果水池满 则阻塞进水线程  等待

#include <stdio.h>
#include <stdlib.h>

#include <semaphore.h>
#include <errno.h>

#include <unistd.h> // sleep 头文件
#include
<pthread.h> pthread_mutex_t g_mutex; sem_t poolin; // 水池容量 信号量 sem_t poolout; // 当前水池容量 信号量 // 线程1 加水 5 void *fnThread1(void *param) { while(1) { int rv = 0; for(int i=0; i<5; ++i) { while( ((rv = sem_wait(&poolin)) != 0) && (errno == EINTR) ); // poolin 信号量减去5 } pthread_mutex_lock(&g_mutex); // 上锁 (*(int *)param) += 5; printf("thread1: %d.\n",*(int*)param); for(int i=0; i<5; ++i) { sem_post(&poolout); // poolout 信号量加5 } pthread_mutex_unlock(&g_mutex); // 解锁 sleep(1); } return NULL; } // 线程2 加水 4 void *fnThread2(void *param) { while(1) { int rv = 0; for(int i=0; i<4; ++i) { while( ((rv = sem_wait(&poolin)) != 0) && (errno == EINTR) ); } pthread_mutex_lock(&g_mutex); (*(int *)param) += 4; printf("thread2: %d.\n",*(int *)param); for(int i=0; i<4; ++i) { sem_post(&poolout); } pthread_mutex_unlock(&g_mutex); sleep(1); } return NULL; } // 线程3 放水 -3 void *fnThread3(void *param) { while(1) { int rv = 0; for(int i=0; i<3; ++i) { while( ((rv = sem_wait(&poolout)) != 0) && (errno == EINTR) ) printf("xx"); } pthread_mutex_lock(&g_mutex); (*(int *)param) -= 3; printf("thread3: %d.\n",*(int *)param); for(int i=0; i<3; ++i) { sem_post(&poolin); } pthread_mutex_unlock(&g_mutex); sleep(1); } return NULL; } int main(int argc, char **argv) { pthread_t th[3]; int value = 0; // 初始化信号量 sem_init(&poolin, 0, 100); // 水池容量初始化为100 sem_init(&poolout, 0, 0); // 水池当前容量初始化为0 pthread_create(&th[0], NULL, fnThread1, (void *)&value); pthread_create(&th[1], NULL, fnThread2, (void *)&value); pthread_create(&th[2], NULL, fnThread3, (void *)&value); // 等待线程退出 for(int i=0; i<3; ++i) pthread_join(th[i], NULL); return 0; }
// 输出结果. 线程永远不会退出 3个线程的执行顺序是一直在变化的
[root@localhost test2]# make
g++ -g -o test main.cpp -lpthread -lrt
[root@localhost test2]# ./test 
thread2: 4.
thread3: 1.
thread1: 6.
thread3: 3.
thread2: 7.
thread1: 12.
thread3: 9.
thread2: 13.
thread1: 18.
thread3: 15.
thread2: 19.
thread1: 24.
thread3: 21.
thread2: 25.
thread1: 30.
thread1: 35.
thread2: 39.
thread3: 36.
thread1: 41.
thread2: 45.
thread3: 42.
thread1: 47.
thread2: 51.
thread3: 48.
thread1: 53.
thread2: 57.
thread3: 54.
thread1: 59.
thread2: 63.
thread3: 60.
thread1: 65.
thread2: 69.
thread3: 66.
thread1: 71.
thread2: 75.
thread3: 72.
thread1: 77.
thread2: 81.
thread3: 78.
thread1: 83.
thread2: 87.
thread3: 84.
thread1: 89.
thread2: 93.
thread3: 90.
thread3: 87.
thread2: 91.
thread1: 96.
thread3: 93.
thread2: 97.
thread3: 94.
thread3: 91.
thread2: 95.
thread1: 100.
thread3: 97.
thread3: 94.
...

3.sem与mutex编写的队列Deque

//参考网址:http://www.cnblogs.com/liuweijian/archive/2009/12/30/1635888.html

#include <iostream>
#include <semaphore.h>
#include <time.h> //


#include <errno.h> // errno EINTR

#include <deque>
using namespace std;

#include "TLock.h"
//#include <pthread.h>

template<typename T>
class SemDeque
{
public:    
    SemDeque(int maxsize)
        :m_MaxSize(maxsize)
    {
        sem_init(&m_enques, 0, maxsize); // 队列初始化容量为 maxsize
        sem_init(&m_deques, 0, 0); // 队列当前容量初始化为0
    }
    
    ~SemDeque()
    {
        sem_destroy(&m_enques);
        sem_destroy(&m_deques);
    }
    
    int sem_wait_time(sem_t *psem, int mswait)
    {
        if(mswait < 0) // 无限等待
        {
            int rv = 0;
            while( ((rv = sem_wait(psem)) != 0) && (errno == EINTR) ); // 等待信号量,errno==EINTR屏蔽其他信号事件引起的等待中断
            
            return rv;    
        }
        else
        {
            timespec tv;

            clock_gettime(CLOCK_REALTIME, &tv); // 获取当前时间
            
            tv.tv_sec += mswait/1000; //
            tv.tv_nsec += (mswait%1000)*1000; // 纳秒
            
            int rv = 0;
            while( ((rv = sem_timedwait(psem, &tv)) != 0) && (errno == EINTR) ); // sem_timedwait 等待信号量,errno==EINTR屏蔽其他信号事件引起的等待中断
            
            return rv;    
        }
    }
    
    bool push_back(const T& item, int mswait = -1)
    {
        if(-1 == sem_wait_time(&m_enques, mswait))
        {
            return false;
        }
        
        m_lock.Lock(); // 上锁
        m_data.push_back(item);
        cout<<"push"<<item<<endl;
        sem_post(&m_deques); // 当前队列信号量 加1
        m_lock.Unlock(); // 解锁
        
        return true;
    }
    
    bool pop_front(T &item, int mswait = -1) // item作为引用,是要传出来的值
    {
        if(-1 == sem_wait_time(&m_deques, mswait))
        {
            return false;
        }
        
        m_lock.Lock(); // 上锁
        item = m_data.front();
        cout<<"pop"<<item<<endl;
        m_data.pop_front();
        sem_post(&m_enques); // 入队列信号量 加1 
        m_lock.Unlock(); // 解锁
        
        return true;
    }
    
    size_t size()
    {
        return m_data.size();
    }
    
private:
    deque<T> m_data;
    
    TLock m_lock;
    
    int m_MaxSize;
    
    sem_t m_enques; // 入队列。
    sem_t m_deques; // 出队列
};
// main.cpp

#include "semDeque.h"

#include <stdio.h> // printf
SemDeque<int> deq(5); // 最多队列里只能有5个

void *pushThread(void *ptr)
{
    for(int i=1; i<=30; ++i)
    {
        deq.push_back(i, -1);
    }
  printf("push Thread exit.\n");
return NULL; } void *popThread(void *ptr) { while(1) { int a = 0; if( !deq.pop_front(a,1000) ) { cout<<"pop failed. size = "<<deq.size()<<endl; } } return NULL; } int main(int argc, char **argv) { pthread_t th[2]; pthread_create(&th[0], NULL, pushThread, NULL); pthread_create(&th[1], NULL, popThread, NULL); pthread_join(th[0], NULL); pthread_join(th[1], NULL); return 0; }
// 输出结果 popThread线程一直不会停止 (每次入队列5个 再出队列5个==>导致这个原因是因为push时输入的时间是-1,直接压入,而pop是要经过1s的延时才输出,所以看到的现象是5个进5个出,假如把时间都改成-1,进出的顺序就不是这样了。)
[root@localhost test3]# make
g++ -g -c TLock.cpp -lpthread
g++ -g -c main.cpp -lpthread -lrt
g++ -g -o test TLock.o main.o -lpthread -lrt
[root@localhost test3]# ./test 
push1
push2
push3
push4
push5
pop1
pop2
pop3
pop4
pop5
push6
push7
push8
push9
push10
pop6
pop7
pop8
pop9
pop10
push11
push12
push13
push14
push15
pop11
pop12
pop13
pop14
pop15
push16
push17
push18
push19
push20
pop16
pop17
pop18
pop19
pop20
push21
push22
push23
push24
push25
pop21
pop22
pop23
pop24
pop25
push26
push27
push28
push29
push30
pop26
pop27
pop28
pop29
pop30
pop failed. size = 0
pop failed. size = 0
pop failed. size = 0
...

线程get_thread每隔1000毫秒从队列取元素,线程put_thread将30个元素依次入队。两个线程模拟两条入队和出队的流水线。因我们在SemDeque<int> qq(5)处定义了队列最多可容纳5个元素,入队线程每入队到队列元素满5个后需阻塞等待出队线程将队列元素出队才能继续。测试时可调整队列可容纳最大元素个数来观察运行效果。 

 

4.cond与mutex的配合使用

  条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线承间的同步。

  假设有共享的资源sum,与之相关联的mutex 是lock_s.假设每个线程对sum的操作很简单的,与sum的状态无关,比如只是sum++.那么只用mutex足够了.程序员只要确保每个线程操作前,取得lock,然后sum++,再unlock即可.每个线程的代码将像这样

add() 
{  
    pthread_mutex_lock(lock_s); 
    sum++; 
    pthread_mutex_unlock(lock_s); 
}

  如果操作比较复杂,假设线程t0,t1,t2的操作是sum++,而线程t3则是在sum到达100的时候,打印出一条信息,并对sum清零. 这种情况下,如果只用mutex, 则t3需要一个循环,每个循环里先取得lock_s,然后检查sum的状态,如果sum>=100,则打印并清零,然后unlock.如果sum& amp; amp; amp; lt;100,则unlock,并sleep()本线程合适的一段时间. 这个时候,t0,t1,t2的代码不变,t3的代码如下:

 
print()  
{  
       while 
      {  
        pthread_mutex_lock(lock_s);  
        if(sum>=100)  
        {  
            printf(“sum reach 100!”);  
            pthread_mutex_unlock(lock_s);  
        }  
        else
        {  
            pthread_mutex_unlock(lock_s);  
            my_thread_sleep(100);  
            return  ;  
        }  
    }  
}

这种办法有三个问题
1) sum在大多数情况下不会到达100,那么对t3的代码来说,大多数情况下,走的是else分支,只是lock和unlock,然后sleep().这浪费了CPU处理时间.
2) 为了节省CPU处理时间,t3会在探测到sum没到达100的时候sleep()一段时间.这样却又带来另外一个问题,亦即t3响应速度下降.可能在sum到达200的时候,t3才醒过来.
3) 这样,程序员在设置sleep()时间的时候陷入两难境地,设置得太短了节省不了资源,太长了又降低响应速度.真是难办啊!

而cond即可很好得解决这个问题:

你首先定义一个condition variable.
pthread_cond_t cond_sum_ready=PTHREAD_COND_INITIALIZER;
t0,t1,t2的代码只要后面加两行,像这样:

add() 
{ 
    pthread_mutex_lock(lock_s); 
    sum++; 
    pthread_mutex_unlock(lock_s); 
    if(sum>=100) 
        pthread_cond_signal(&cond_sum_ready);  // 符合条件>=100,唤醒线程
} 

t3线程:

print 
{ 
    pthread_mutex_lock(lock_s); 
    while(sum<100) 
        pthread_cond_wait(&cond_sum_ready, &lock_s);  // 等待满足条件
    printf(“sum is over 100!”); 
    sum=0; 
    pthread_mutex_unlock(lock_s); 
    return;
}

注意两点:
1) 在thread_cond_wait()之前,必须先lock相关联的mutex, 因为假如目标条件未满足,pthread_cond_wait()实际上会unlock该mutex, 然后block(阻塞),在目标条件满足后再重新lock该mutex, 然后返回.
2) 为什么是while(sum<100),而不是if(sum<100) ?这是因为在pthread_cond_signal()和pthread_cond_wait()返回之间,有时间差,假设在这个时间差内,还有另外一个线程t4又把sum减少到100以下了,那么t3在pthread_cond_wait()返回之后,显然应该再检查一遍sum的大小.
这就是用 while的用意。

 完整例子:
#include <stdio.h>
#include <stdlib.h>

#include <pthread.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int a = 0;

// 线程1 负责叠加a的值 当a满足条件时唤醒线程2
void *fnThread1(void *ptr)
{
    printf("enter thread 1.\n");
    while(1)
    {        
        pthread_mutex_lock(&mutex);
        a++;
        printf("a = %d\n",a);
        pthread_mutex_unlock(&mutex);
    
        if(a >= 100)
        {
            pthread_cond_signal(&cond); // 条件唤醒    
            break;
        }
    }
    printf("exit thread 1.\n");    
    return NULL;
}

// 线程2 当a不满足条件时则 条件阻塞。
void *fnThread2(void *ptr)
{
    printf("enter thread 2.\n");
    pthread_mutex_lock(&mutex);
    while(a < 100)
    {
        pthread_cond_wait(&cond, &mutex);
    }
    printf("a >= 100 conditions are met.\n");
    
    pthread_mutex_unlock(&mutex);
        
    printf("exit thread 2.\n");
    return NULL;
}

int main(int argc, char **argv)
{
    pthread_t t1,t2;

    pthread_create(&t1, NULL, fnThread1, NULL);
    pthread_create(&t2, NULL, fnThread2, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    printf("main exit.\n");

    return 0;    
}

 

 
最后可参考下别人写的线程锁模板类
http://www.cppblog.com/qinqing1984/archive/2014/12/28/209327.html