Linux 多线程编程之线程同步
作者 斯人 | 发布于 2013 年 2 月 21 日
Linux C

Linux 多线程编程之线程同步

在同一个内存空间中是用多线程,要有一个基本原则:“线程之间的数据不能相互破坏”

例如:

有两个线程要修改两个变量,一个线程把两个变量都设置为0,另外一个都设置为1,最终的结果可能会是其中一个变量的值是1,

另外一个是0,这就是因为运行环境不断切换造成的。

POSIX标准为我们提供了两种同步机制:“互斥锁”“条件变量”

互斥锁

所谓互斥锁就是 当有一个线程访问数据时,会把当前的数据 “锁定”,在这个线程解锁之前,没有其他线程可以锁定(成功锁定)此变量。

如果一个线程已经锁定了互斥锁,又有第二个线程来锁定互斥锁,这个时候第二个线程会挂起,直到第一个线程解锁互斥锁。然而由互斥锁

只有两种状态,这在使用时会很不方便,局限性太小。

1、互斥锁初始化

pthread_mutex_t mutex_t=PTHREAD_MUTEX_INITIALIZER;

int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr *attr);

这两种都可以初始化。

第二个:

pthread_mutex_attr *attr :互斥锁的属性,目前先设置为NULL默认值。

注意“一个锁只能初始化一次”,该函数执行成功0,并将新创建的互斥锁的ID放到mutex中,执行失败则返回错误编号。

2、互斥锁释放

int pthread_mutex_destroy(pthread_mutex_t *mutex);

3、lock

int pthread_mutex_lock(pthread_mutex_t *mutex);

该函数可以锁定mutex指向的互斥锁,如果mutex已经被锁定,那么调用该函数的线程将会被阻塞,直到互斥锁被其他线程释放为止,

当pthread_mutex_lock返回的时候,说明加锁成功。

4、trylock

int pthread_mutex_trylock(pthread_mutex_t *mutex);

该函数可以尝试锁定mutex指向的互斥锁,如果发现该互斥锁已经被锁,则该函数不会阻塞,并返回一个错误,否则互斥锁将被调用者加锁。

5、unlock

int pthread_mutex_unlock(pthread_mutex_t *unlock);

该函数对Mutex指向的互斥锁进行解锁操作。成功返回0,其他值则失败。

该函数有个原则,谁加的锁由谁解锁,解铃还须系铃人,不然unlock会失败。

实例:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
typedef struct _thread_entry{
    int num;
    pthread_mutex_t lock;
}thread_entry;

void *thread1(void *arg){
    thread_entry *t=(thread_entry*)arg;
    pthread_mutex_lock(&t->lock);
    t->num=200; 
    printf("%s\n","thread 1 is running");
    pthread_mutex_unlock(&t->lock);
}
void *thread2(void *arg){
    thread_entry *t=(thread_entry*)arg;
    pthread_mutex_lock(&t->lock);
    t->num=10;
    printf("%s\n","thread 2 is running");
    pthread_mutex_unlock(&t->lock);
}

int main(){
    pthread_t tid[2];
    char *ret;
    thread_entry *te=(thread_entry*)malloc(sizeof(thread_entry));
    te->num=10;
    pthread_mutex_init(&te->lock,NULL);
    pthread_create(&tid[0],NULL,(void*)thread1,(void*)te);
    pthread_create(&tid[1],NULL,(void*)thread2,(void*)te);

    pthread_join(tid[0],NULL);
    pthread_join(tid[1],NULL);
    pthread_mutex_destroy(&te->lock);
    return 0;
}

条件变量

条件变量是互斥锁的补充,它允许线程阻塞并等待另一个线程发送信号,收到信号时,阻塞的线程被唤醒并尝试锁定相关的互斥锁。

条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止,通常条件变量和互斥锁同时使用。

1.初始化

条件变量的初始化也有两种方式

pthread_cond_t t=PTHREAD_COND_INITIALIZER;

int pthread_cond_init(pthread_cond_t *t,pthread_cond_attr *att);

成功返回0,其他则出错

第二个是条件变量的属性,这里设为NULL。

2、释放条件变量

int pthread_cond_destroy(pthread_cond_t *t);

成功返回0,其他出错。

3、阻塞在条件变量上

int pthread_cond_wait(pthread_cond_t *t,pthread_mutex_t *mutex);

该函数将解锁*mutex指向的互斥锁,并使当前线程阻塞在*t指向的条件变量上,

被阻塞的信号会被三种情况唤醒

调用pthread_cond_signal,pthread_cond_broadcast和中断信号。

有两点也需要注意:

pthread_cond_wait函数的返回并不意味着条件的值一定发生了变化,必须重新检查条件的值。

pthread_cond_wait函数返回时但还没有返回,相应的互斥锁将被当前线程锁定,即使是函数出错返回,

所以在函数返回之后,在锁定互斥锁之前,要重新测试条件值,最好的办法是 循环调用pthread_mutex_wait()函数


pthread_cond_t cond_t;
pthread_mutex_lock(&mutex);
while(!cond_t)
      pthread_cond_wait();
pthread_mutex_unlock();

int pthread_cond_timedwait(pthread_cond_t *t,pthread_mutex_t *mutex,cont struct timespec *abstime);
成功返回0,其他出错。

该函数允许我们设置阻塞当前线程的时间,如果超过设置的时间,则会解除当前阻塞状态,即便没有条件产生。

函数返回时,互斥锁的状态是已锁。

4、发送条件信号

int pthread_cond_signal(pthread_cond_t *t);

成功返回0,其他出错。

该函数会解除条件变量上的阻塞。

如果没有线程阻塞在该条件变量上,则调此函数不起作用。

5、释放阻塞在该条件的所有线程

int pthread_cond_broadcast(pthread_cond_t *t);

成功返回0,其他出错。

该函数将唤醒所有阻塞在条件变量t下的线程,如果没有线程阻塞在t条件下,调用该函数则不起作用。

实例:

下面是一个简单的生成和消费者的例子,Product负责生产,当生产10个的时候,Consumer则消费到0.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define MAXLIN 10
pthread_cond_t cond_t;
pthread_mutex_t mutex_t;
int product=0;
void *producer(void *arg){
        pthread_mutex_lock(&mutex_t);

        while(product>0){
                pthread_cond_wait(&cond_t,&mutex_t);
        }
        while(product<=MAXLIN){
                product++;
                printf("Producer:%d\n",product);
        }
        pthread_cond_signal(&cond_t);
        pthread_mutex_unlock(&mutex_t);
}
void *consumer(void *arg){
        pthread_mutex_lock(&mutex_t);

        while(!(product>0)){
                pthread_cond_wait(&cond_t,&mutex_t);
        }
        while(product>0){
                product--;
                printf("Consumer:%d\n",product);
        }
        pthread_cond_signal(&cond_t);
        pthread_mutex_unlock(&mutex_t);
}
int main(){
        pthread_mutex_init(&mutex_t,NULL);
        pthread_cond_init(&cond_t,NULL);

        pthread_t t[2];
        pthread_create(&t[0],NULL,(void*)producer,NULL);
        pthread_create(&t[1],NULL,(void*)consumer,NULL);

        //pthread_cond_signal(&cond_t); 
        pthread_join(t[0],NULL);
        pthread_join(t[1],NULL);

        pthread_cond_destroy(&cond_t);;

        pthread_mutex_destroy(&mutex_t);
}

编译好后执行,如下结果:

原文出处:http://www.imsiren.com/archives/758