多线程的三种同步机制

1,互斥锁;

  这个感觉只是保护了资源不会被同时使用,至于同步???还没怎么理解。

  基本函数:  

  通过锁机制实现线程间的同步。同一时刻只允许一个线程执行一个关键部分的代码。

  int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr); 或pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIER。前者是动态初始化,后者是静态。

  int pthread_mutex_lock(pthread_mutex *mutex);

  int pthread_mutex_destroy(pthread_mutex *mutex);

  int pthread_mutex_unlock(pthread_mutex *

  这个用得比较多,不做详细说明。

2,条件变量

   条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。

  基本函数:

  初始化:同互斥锁一样也是两种方式 pthread_cond_t cond=PTHREAD_COND_INITIALIER和int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);  

  int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex); //等待条件变量

  int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime); //延时等待条件变量

  int pthread_cond_destroy(pthread_cond_t *cond); //删除条件变量

  int pthread_cond_signal(pthread_cond_t *cond); //解除在一个等待的线程阻塞

  int pthread_cond_broadcast(pthread_cond_t *cond);  //解除所有线程的阻塞

  看个例子:(此例子着重说明pthre_cond_signal/broadcast,如果没有wait,依旧正常返回,并且不会保留到下次wait,通俗点:我解除阻塞的时候,你没有阻塞,那哥们我先走了,下次你阻塞,得等到我下次来解除,过时不候~~)

#include <pthread.h>
#include <stdio.h>
#include <sys/signal.h>
#include <semaphore.h>  


static pthread_mutex_t vpu_mutex1 = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t vpu_mutex2 = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t vpu_cond1 = PTHREAD_COND_INITIALIZER;
static pthread_cond_t vpu_cond2 = PTHREAD_COND_INITIALIZER;

void* test_thread1(void *arg)
{
    sleep(2);
    pthread_mutex_lock(&vpu_mutex1);
    pthread_cond_signal(&vpu_cond1);
    pthread_mutex_unlock(&vpu_mutex1);

sleep(5);
    
}

void* test_thread2(void *arg)
{
    sleep(1);
    pthread_mutex_lock(&vpu_mutex2);
    pthread_cond_signal(&vpu_cond2);
    pthread_mutex_unlock(&vpu_mutex2);
    sleep(5);
}

int main()
{
    int i, j, ret;
        
    pthread_t test_id1;
    pthread_t test_id2;

    ret = pthread_create(&test_id1, NULL, test_thread1, NULL);
    if (ret != 0)
    {
        printf("Create pthread error!
");
        return -1;//exit(1);
    }

    ret = pthread_create(&test_id2, NULL, test_thread2, NULL);
    if (ret != 0)
    {
        printf("Create pthread error!
");
        return -1;
        //exit(1);
    }
    pthread_mutex_lock(&vpu_mutex1);
    pthread_cond_wait(&vpu_cond1, &vpu_mutex1);
    pthread_mutex_unlock(&vpu_mutex1);

    pthread_mutex_lock(&vpu_mutex2);
    pthread_cond_wait(&vpu_cond2, &vpu_mutex2);
    pthread_mutex_unlock(&vpu_mutex2);
    
    printf("hello world
");
    pthread_join(test_id1, NULL);

    return 0;
}

上面的例子是不会打印出hello world的。因为等不到vpu_cond2的接触。如果将test_thread1和test_thread2中的sleep对调,就可以了。 这个简单,将多了就啰嗦了。

说明:

   (1)pthread_cond_wait 自动解锁互斥量(如同执行了pthread_unlock_mutex),并等待条件变量触发。这时线程挂起,不占用CPU时间,直到条件变量被触发(变量为ture)。在调用pthread_cond_wait之前,应用程序必须加锁互斥量。pthread_cond_wait函数返回前,自动重新对互斥量加锁(如同执行了pthread_lock_mutex)。

    (2)互斥量的解锁和在条件变量上挂起都是自动进行的。因此,在条件变量被触发前,如果所有的线程都要对互斥量加锁,这种机制可保证在线程加锁互斥量和进入等待条件变量期间,条件变量不被触发。条件变量要和互斥量相联结,以避免出现条件竞争——个线程预备等待一个条件变量,当它在真正进入等待之前,另一个线程恰好触发了该条件(条件满足信号有可能在测试条件和调用pthread_cond_wait函数(block)之间被发出,从而造成无限制的等待)。

3,信号量

  #include <semaphore.h>

  int sem_init (sem_t *sem , int pshared, unsigned int value);

      这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE

  两个原子操作函数:

  int sem_wait(sem_t *sem);

  int sem_post(sem_t *sem);

  这两个函数都要用一个由sem_init调用初始化的信号量对象的指针做参数。

  sem_post:给信号量的值加1

  sem_wait:给信号量减1;对一个值为0的信号量调用sem_wait,这个函数将会等待直到有其它线程使它不再是0为止。

  int sem_getvalue(sem_t *sem, int *sval);

  获取信号的值,存入sval中。

  int sem_destroy(sem_t *sem);

  这个函数的作用是再我们用完信号量后都它进行清理。归还自己占有的一切资源。

  

  看个例子:跟上面那个一样,但是信号是可以保留的,因为sem_post后,信号量+1,sem_wait信号量-1,即只要sem不为0,一遇wait就向下执行。

  

#include <pthread.h>
#include <stdio.h>
#include <sys/signal.h>
#include <semaphore.h> 

sem_t sem_1;
sem_t sem_2;

void* test_thread1(void *arg)
{
    sleep(3);
sem_post(&sem_1);

sleep(5);
    
}

void* test_thread2(void *arg)
{
    sleep(1);
    sem_post(&sem_2);
    sleep(5);
}

int main(int argc, char **argv)
{
    int i, j, ret;
    
    sem_init(&sem_1, 0, 0);
    sem_init(&sem_2, 0, 0);

    pthread_t test_id1;
    pthread_t test_id2;

    ret = pthread_create(&test_id1, NULL, test_thread1, NULL);
    if (ret != 0)
    {
        printf("Create pthread error!
");
        return -1;//exit(1);
    }

    ret = pthread_create(&test_id2, NULL, test_thread2, NULL);
    if (ret != 0)
    {
        printf("Create pthread error!
");
        return -1;
        //exit(1);
    }

    

    sem_wait(&sem_1);
    sem_wait(&sem_2);
//    sem_getvalue(&single_seq, &sem_value);
    
    printf("hello world
");
    pthread_join(test_id1, NULL);


}

通过上面两个例子对比,可得条件变量和信号量的区别。当然如果要使得条件变量,具有信号量的功能。可以这么做:

再定义一个全局变量flag,判断flag的值,比如 在发送端,先flag++,在pthread_cond_signal; 在接收端while(flag<2)pthread_cond_wait();

这样就ok了。为什么不使用一个全局变量while(flag<2);呢?因为这样很占用CPU,为什么不while(flag<2)usleep(timecnt);呢?

在线程里面sleep函数是不安全的!下篇接受,并解决线程延时问题。

原文地址:https://www.cnblogs.com/cyc2009/p/3974348.html