线程控制(二)

xiaoxiao2021-02-28  101

线程同步

互斥锁(不占内存)

互斥锁(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 hello world *不上锁 [limeng@KID 8.1]$ gcc huchisuo.c -lpthread [limeng@KID 8.1]$ ./a.out hweolrlldi thread1上锁,thread2不上锁 [limeng@KID 8.1]$ ./a.out hweolrllod */ //可见使用互斥锁就是为了让某一资源在一段时间内一个进程被独占,从而不混乱,但是一个线程对某一资源上锁,其它没有上锁的资源还可以对其操作。所以,互斥锁又叫建议锁,或者协同锁。但不是强制性的。

条件变量 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先不考虑,大致就是这样)*/

异步信号

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

//向特定的线程发送信号signo int 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);

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

转载请注明原文地址: https://www.6miu.com/read-65333.html

最新回复(0)