线程同步

来源:互联网 发布:种子搜索 知乎 编辑:程序博客网 时间:2024/05/21 08:41

当多个控制线程共享相同的内存时,需要确保每个线程看到一致的数据视图。原因在于,当多个线程同时读取和修改变量时,会造成共享变量的不一致,因此需要对线程进行同步,确保他们在访问变量的存储内容时不会访问到无效的值。

互斥量

可以使用pthread的互斥接口来保护数据,确保同一时间只有一个线程访问数据。互斥量(mutex)本质上是一把锁,在访问共享资源前对互斥量进行设置(加锁),在访问结束后释放(解锁)互斥量。对互斥量加锁以后,任何其他试图再次对互斥量加锁的线程都会被堵塞,直到当前线程释放该互斥锁。如果释放互斥量时有一个以上的线程阻塞,那么所有的该锁上的阻塞线程都会变成可运行状态,第一个变为运行的线程可以对互斥量加锁,其他线程就会看到互斥量是锁着的,只能再次阻塞直到互斥量变为可用。这种方式下,每次只有一个线程可以执行。
只有将所有线程都设计成遵守相同数据访问规则的,互斥机制才能正常工作。操作系统并不为我们做数据访问的串行化。如果允许某个线程在没有得到锁的情况下也可以访问共享资源,那么即使其他的线程在使用共享资源前都申请锁,也还是会出现数据不一致的问题。
互斥变量是用pthread_mutex_t数据类型表示。在使用互斥变量以前,必须首先对它进行初始化,可以把它设置为常量PTHREAD_MUTEX_INITIALIZER(只适用于静态分配的互斥量),也可以调用pthread_mutex_init函数进行初始化。如果动态分配互斥量(例如,通过调用malloc函数),在释放内存前需要调用pthread_mutex_destory。

#include <pthread.h>pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);int pthread_mutex_lock(pthread_mutex_t *mutex);int pthread_mutex_trylock(pthread_mutex_t *mutex);int pthread_mutex_unlock(pthread_mutex_t *mutex);int pthread_mutex_destroy(pthread_mutex_t *mutex);-------------------------------------------------pthread_mutex_init always returns 0. The other mutex functions return 0 on success and a non-zero error code on error.

pthread_mutex_trylock尝试对互斥量加锁,成功返回0,否则不能加互斥锁,返回EBUSY。

#include <stdio.h>#include <stdlib.h>#include <pthread.h>typedef struct foo{    int             f_count;    pthread_mutex_t f_lock;    int             f_id;    /* ... more stuff here ...*/} foo;/* allocate the object */foo*foo_alloc(int id) {    foo *fp;    if((fp = malloc(sizeof(foo))) != NULL)    {        fp->f_count = 1;        fp->f_id = id;        if(pthread_mutex_init(&fp->f_lock, NULL) != 0)        {            free(fp);            return NULL;        }    }    return fp;}/* add a reference to the object */voidfoo_hold(foo *fp){    pthread_mutex_lock(&fp->f_lock);    fp->f_count ++;    pthread_mutex_unlock(&fp->f_lock);}/* release a reference tp the object */voidfoo_rele(foo *fp){    pthread_mutex_lock(&fp->f_lock);    if(--fp->f_count == 0)    {        pthread_mutex_unlock(&fp->f_lock);        pthread_mutex_destroy(&fp->f_lock);    }    else    {        pthread_mutex_unlock(&fp->f_lock);    }}

避免死锁

  • 如果线程尝试对同一个互斥锁加锁两次,那么自身会陷入死锁状态;
  • 程序中使用一个以上的互斥量时,如果允许一个线程一直占有第一个互斥量,并且在尝试锁住第二个互斥量时处于阻塞状态,但是拥有第二个互斥量的线程也在试图锁住第一个互斥量,因为两个线程都在相互请求另一个线程拥有的资源,所以这两个线程都无法向前运行,于是就产生死锁;

避免死锁的方法

  • 仔细控制互斥量加锁的顺序来避免死锁的发生;
  • 使用pthread_mutex_trylock接口避免死锁;

线程退出时并不会释放当前持有且未释放的mutex锁,因此当其他线程在申请此锁时会被阻塞;只有当线程所在的进程退出时,mutex锁才会被操作系统释放。

// Example:zhuqingping@ubuntu:~/Study/C/pthread$ cat thread_exit_without_release_lock.c #include <pthread.h>#include <stdio.h>#include <stdlib.h>pthread_mutex_t fastmutex;void *my_new_thread(void *args){    printf("New Thread Start\n");    pthread_mutex_lock(&fastmutex);    printf("New Thread End\n");    return NULL;}int main(){    pthread_t new_thread;    pthread_mutex_init(&fastmutex, NULL);    if(pthread_create(&new_thread, NULL, my_new_thread, NULL) != 0)    {        printf("new thread create error\n");        return -1;    }    sleep(2);    pthread_mutex_lock(&fastmutex);    printf("Main Thread\n");    pthread_mutex_unlock(&fastmutex);    pthread_mutex_destroy(&fastmutex);    return 0;}zhuqingping@ubuntu:~/Study/C/pthread$ ./thread_exit_without_release_lock New Thread StartNew Thread End^Czhuqingping@ubuntu:~/Study/C/pthread$
0 0
原创粉丝点击