线程控制(二)

来源:互联网 发布:美容院软件 编辑:程序博客网 时间:2024/06/06 04:48

线程同步

互斥锁(不占内存)

互斥锁(mutex),互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。在同一时刻它通常只允许一个线程执行一个关键部分的代码。

初始化:

初始化互斥锁的两种方式:

//静态赋值法pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIAER;//通过pthread_mutex_init函数初始化int pthread_mutex_init(pthread_mutex_t *mutex,                    constt pthread_mutexattr_t *mutexatter);mutexatter表示互斥锁的属性,NULL则使用默认属性

互斥锁的属性
* PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。

  • PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。

  • PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。

  • PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

加锁:
int pthread_mutex_lock(pthread_mutex_t *mutex);//阻塞申请互斥锁int pthread_mutex_trylock(pthread_mutex_t *mutex);//非阻塞申请互斥锁

pthread_mutex_lock()加锁时,如果mutex已被锁住,当前尝试加锁的线程就会阻塞,直到互斥锁被其它线程释放。当pthread_mutex_lock()函数返回时,说明互斥锁已经被当前进程成功加锁。

pthread_mutex_trylock()加锁时,如果mutex已被锁住,它将立即返回,返回错误代码EBUSY,而不是阻塞等待

attention:不论哪种类型的锁,都不可能被两个线程同时得到,其中一个必须等待解锁。在同一进程的线程,如果加锁后没有解锁,则其他进程无法再获得该锁。

解锁

int pthread_mutex_unlock(pthread_mutex_t *mutex);

解锁要满足的条件:
1、互斥锁要处于加锁状态
2、调用该函数的线程必须是给互斥锁加锁的线程
(解锁后如果有其它进程正在等待互斥锁,等待队列的第一个线程会获得该互斥锁)

清除

int pthread_mutex_destroy(pthread_mutex_t *mutex);

释放锁的资源,清除锁要求锁当前处于开放状态。若锁处于锁定状态,函数返回EBUSY,该函数执行成功返回0。

互斥锁实例:打印机

#include <stdio.h>#include <pthread.h>#include <unistd.h>pthread_mutex_t mutex; //互斥锁// 打印机,一段时间内只能被一个用户使用,不能被多人同时使用void printer(char *str){    pthread_mutex_lock(&mutex); //上锁    while(*str!='\0')    {        putchar(*str);        fflush(stdout);        str++;        sleep(1);    }    printf("\n");    pthread_mutex_unlock(&mutex); //解锁}void printer2(char *str){    //pthread_mutex_lock(&mutex); //上锁    while(*str!='\0')    {        putchar(*str);        fflush(stdout);        str++;        sleep(1);    }    printf("\n");    //pthread_mutex_unlock(&mutex); //解锁}// 线程一void *thread_fun_1(void *arg){    char *str = "hello";    printer(str); //打印}// 线程二void *thread_fun_2(void *arg){    char *str = "world";    printer(str);    //printer2(str); //打印}int main(void){    pthread_t tid1, tid2;    pthread_mutex_init(&mutex, NULL); //初始化互斥锁    // 创建 2 个线程    pthread_create(&tid1, NULL, thread_fun_1, NULL);    pthread_create(&tid2, NULL, thread_fun_2, NULL);    // 等待线程结束,回收其资源    pthread_join(tid1, NULL);    pthread_join(tid2, NULL);    pthread_mutex_destroy(&mutex); //销毁互斥锁    return 0;}/* * 上锁[limeng@KID 8.1]$ gcc huchisuo.c  -lpthread[limeng@KID 8.1]$ ./a.out helloworld *不上锁 [limeng@KID 8.1]$ gcc huchisuo.c  -lpthread[limeng@KID 8.1]$ ./a.out hweolrlldithread1上锁,thread2不上锁[limeng@KID 8.1]$ ./a.outhweolrllod*///可见使用互斥锁就是为了让某一资源在一段时间内一个进程被独占,从而不混乱,但是一个线程对某一资源上锁,其它没有上锁的资源还可以对其操作。所以,互斥锁又叫建议锁,或者协同锁。但不是强制性的。

条件变量
1.一个等待使用资源的线程等待“条件变量被设置为真”
2.另一个线程在使用完资源后“设置条件为真”

初始化
//静态赋值法pthread_cond_t cond=PTHREAD_COND_INITIALIER;//使用init函数int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);  

cond_attr参数是条件变量的属性,由于其并没有得到实现,所以它的值通常为NULL

等待条件成立
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);

pthread_cond_wait函数释放由mutex指向的互斥锁,同时当前进程关于cond指向的条件变量阻塞,直到条件信号被唤醒。

pthread_cond_timewait将阻塞直到条件变量获得信号或者经由abstime指定的时间。如果在指定时间结束都没有条件满足,则返回ETIMEOUT,结束等待。

解除阻塞
int pthread_cond_signal(pthread_cond_t *cond);int pthread_cond_broadcast(pthread_cond_t *cond); 

pthread_cond_signal激活一个等待条件成立的线程,存在多个等待线程时,安入队顺序激活
pthread_cond_broadcast激活所有等待条件的线程

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

只有在没有线程等待该条件变量的时候才能清除这个条件变量,否则返回EBUSY

#include<stdio.h>#include<string.h>#include<stdlib.h>#include<unistd.h>#include<pthread.h>pthread_mutex_t mutex;pthread_cond_t cond;void * thread1(void * arg){    pthread_cleanup_push((void *)pthread_mutex_unlock,&mutex);    while(1){        printf("thread1 is running\n");        pthread_mutex_lock(&mutex);        pthread_cond_wait(&cond,&mutex);        printf("thread1 applied the condition\n");        pthread_mutex_unlock(&mutex);        printf("unlock\n");        printf("\n");        sleep(1);    }    pthread_cleanup_pop(0);}void *thread2(void * arg){    while(1){        printf("thread2 is running\n");        pthread_mutex_lock(&mutex);        printf("thread2  get\n");        pthread_cond_wait(&cond,&mutex);        printf("thread2 applied the condition\n");        pthread_mutex_unlock(&mutex);        printf("unlock\n");        printf("\n");        sleep(1);    }}int main(void){    pthread_t tid1,tid2;    printf("condition variable study!\n");    pthread_mutex_init(&mutex,NULL);    pthread_cond_init(&cond,NULL);    pthread_create(&tid1,NULL,(void *)thread1,NULL);    pthread_create(&tid2,NULL,(void *)thread2,NULL);    do{        sleep(2);        pthread_cond_signal(&cond);    }while(1);    sleep(50);    pthread_exit(0);}/*pthread_cleanup_push((void *)pthread_mutex_unlock, (void *) &mut);pthread_mutex_lock(&mut);// do some work pthread_mutex_unlock(&mut);pthread_cleanup_pop(0);本来do some work之后是有pthread_mutex_unlock(&mut);这句,也就是有解锁操作,但是在do some work时会出现非正常终止,那样的话,系统会根据pthread_cleanup_push中提供的函数,和参数进行解锁操作或者其他操作,以免造成死锁!*//*在这里是thread1先上锁,然后通过pthread_cond_wait使其阻塞,进入等待队列,释放锁;这时thread2开始上锁,接着也因为条件变量阻塞并释放锁,进入等待队列。这时主线程通过pthread_cond_signal激活一个等待条件成立的线程,这时thread1在等待队列的前面,所以thread1被激活,这时互斥锁重新被锁上,然后进行完之后,解锁。然后解锁,上锁,条件变量阻塞再次进入等待队列,接下来该激活thread2,以此类推(sleep先不考虑,大致就是这样)*/

异步信号

信号与任何线程通信都是异步的。信号到达的时间是不定的。如果有多个线程接受信号,只有一个被选中。如果并发的多个信号被送到一个进程,每一个将被不同的线程处理。如果所有线程都屏蔽该信号,则这些信号被挂起,直到有信号解除屏蔽来处理它们。

//向特定的线程发送信号signoint pthread_kill(pthread_t threadid,int signo);//设置线程的信号屏蔽码int pthread_sigmask(int how,const sigset_t *newmask,sigset_t *oldmask);//阻塞线程int sigwait(const sigset_t *set,int *sig);

关于信号,以后到信号那一章再仔细研究。

原创粉丝点击