嵌入式 linux下多线程同步问题

2019-07-12 18:35发布

现在流行的进程线程同步互斥的控制机制,其实是由最原始最基本的4种方法实现的。由这4种方法组合优化就有了.Net和Java下灵活多变的,编程简便的线程进程控制手段。
1临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
2互斥量:为协调共同对一个共享资源的单独访问而设计的。
3信号量:为控制一个具有有限数量用户资源而设计。
4事件:用来通知线程有一些事件已发生,从而启动后继任务的开始。
线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点。linux下提供了多种方式来处理线程同步,最常用的是互斥锁、条件变量和信号量。 1)互斥锁(mutex     通过锁机制实现线程间的同步。同一时刻只允许一个线程执行一个关键部分的代码。 int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr); int pthread_mutex_lock(pthread_mutex *mutex); int pthread_mutex_destroy(pthread_mutex *mutex); int pthread_mutex_unlock(pthread_mutex * (1)先初始化锁init()或静态赋值pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIER attr_t有: PTHREAD_MUTEX_TIMED_NP:其余线程等待队列 PTHREAD_MUTEX_RECURSIVE_NP:嵌套锁,允许线程多次加锁,不同线程,解锁后重新竞争 PTHREAD_MUTEX_ERRORCHECK_NP:检错,与一同,线程请求已用锁,返回EDEADLK; PTHREAD_MUTEX_ADAPTIVE_NP:适应锁,解锁后重新竞争 (2)加锁,lock,trylock,lock阻塞等待锁,trylock立即返回EBUSY (3)解锁,unlock需满足是加锁状态,且由加锁线程解锁 (4)清除锁,destroy(此时锁必需unlock,否则返回EBUSY,//Linux下互斥锁不占用内存资源 示例代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #include   #include   #include   #include   #include "iostream"          using namespace std;          pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;   int tmp;          void* thread(void *arg)   {       cout << "thread id is " << pthread_self() << endl;       pthread_mutex_lock(&mutex);       tmp = 12;       cout << "Now a is " << tmp << endl;       pthread_mutex_unlock(&mutex);       return NULL;   }          int main()   {       pthread_t id;       cout << "main thread id is " << pthread_self() << endl;       tmp = 3;       cout << "In main func tmp = " << tmp << endl;       if (!pthread_create(&id, NULL, thread, NULL))       {           cout << "Create thread success!" << endl;       }       else       {           cout << "Create thread failed!" << endl;       }       pthread_join(id, NULL);       pthread_mutex_destroy(&mutex);       return 0;   } 编译: g++ -o thread testthread.cpp -lpthread 说明:pthread库不是Linux系统默认的库,连接时需要使用静态库libpthread.a,所以在使用pthread_create()创建线程,以及调用pthread_atfork()函数建立fork处理程序时,需要链接该库。在编译中要加 -lpthread参数。 2)条件变量(cond     利用线程间共享的全局变量进行同步的一种机制。条件变量上的基本操作有:触发条件(当条件变为 true );等待条件,挂起线程直到其他线程触发条件。 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);  //解除所有线程的阻塞 (1)初始化.init()或者pthread_cond_t cond=PTHREAD_COND_INITIALIER(前者为动态初始化,后者为静态初始化);属性置为NULL (2)等待条件成立.pthread_wait,pthread_timewait.wait()释放锁,并阻塞等待条件变量为真,timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait) (3)激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程) (4)清除条件变量:destroy;无线程等待,否则返回EBUSY 对于 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); 一定要在mutex的锁定区域内使用     如果要正确的使用pthread_mutex_lockpthread_mutex_unlock,请参考 pthread_cleanup_push和pthread_cleanup_pop宏,它能够在线程被cancel的时候正确的释放mutex     另外,posix1标准说,pthread_cond_signalpthread_cond_broadcast无需考虑调用线程是否是mutex的拥有者,也就是说,可以在lockunlock以外的区域调用。如果我们对调用行为不关心,那么请在lock区域之外调用吧。 说明:     (1)pthread_cond_wait 自动解锁互斥量(如同执行了pthread_unlock_mutex),并等待条件变量触发。这时线程挂起,不占用CPU时间,直到条件变量被触发(变量为ture)。在调用 pthread_cond_wait之前,应用程序必须加锁互斥量。pthread_cond_wait函数返回前,自动重新对互斥量加锁(如同执行了pthread_lock_mutex)     (2)互斥量的解锁和在条件变量上挂起都是自动进行的。因此,在条件变量被触发前,如果所有的线程都要对互斥量加锁,这种机制可保证在线程加锁互斥量和进入等待条件变量期间,条件变量不被触发。条件变量要和互斥量相联结,以避免出现条件竞争——个线程预备等待一个条件变量,当它在真正进入等待之前,另一个线程恰好触发了该条件(条件满足信号有可能在测试条件和调用pthread_cond_wait函数(block)之间被发出,从而造成无限制的等待)。 (3)pthread_cond_timedwait 和 pthread_cond_wait 一样,自动解锁互斥量及等待条件变量,但它还限定了等待时间。如果在abstime指定的时间内cond未触发,互斥量mutex被重新加锁,且pthread_cond_timedwait返回错误 ETIMEDOUTabstime 参数指定一个绝对时间,时间原点与 time gettimeofday 相同:abstime = 0 表示 19701100:00:00 GMT (4)pthread_cond_destroy 销毁一个条件变量,释放它拥有的资源。进入 pthread_cond_destroy 之前,必须没有在该条件变量上等待的线程。     (5)条件变量函数不是异步信号安全的,不应当在信号处理程序中进行调用。特别要注意,如果在信号处理程序中调用 pthread_cond_signal pthread_cond_boardcast 函数,可能导致调用线程死锁。 示例程序1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include #include #include "stdlib.h" #include "unistd.h"   pthread_mutex_t mutex; pthread_cond_t cond;   void hander(void *arg) {     free(arg);     (void)pthread_mutex_unlock(&mutex); }   void *thread1(void *arg) {      pthread_cleanup_push(hander, &mutex);      while(1)      {          printf("thread1 is runningn");          pthread_mutex_lock(&mutex);          pthread_cond_wait(&cond,&mutex);          printf("thread1 applied the conditionn");          pthread_mutex_unlock(&mutex);          sleep(4);      }      pthread_cleanup_pop(0); }   void *thread2(void *arg) {     while(1)     {         printf("thread2 is runningn");         pthread_mutex_lock(&mutex);         pthread_cond_wait(&cond,&mutex);         printf("thread2 applied the conditionn");         pthread_mutex_unlock(&mutex);         sleep(1);     } }   int main() {      pthread_t thid1,thid2;      printf("condition variable study!n");      pthread_mutex_init(&mutex,NULL);      pthread_cond_init(&cond,NULL);      pthread_create(&thid1,NULL,thread1,NULL);      pthread_create(&thid2,NULL,thread2,NULL);      sleep(1);      do      {          pthread_cond_signal(&cond);      }while(1);      sleep(20);      pthread_exit(0);      return 0; } 示例程序2: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #include #include #include "stdio.h" #include "stdlib.h"   static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;   struct node {      int n_number;      struct node *n_next; } *head = NULL;   /*[thread_func]*/ static void cleanup_handler(void *arg) {      printf("Cleanup handler of second thread./n");      free(arg);      (void)pthread_mutex_unlock(&mtx); }   static void *thread_func(void *arg) {      struct node *p = NULL;      pthread_cleanup_push(cleanup_handler, p);      while (1)      {          //这个mutex主要是用来保证pthread_cond_wait的并发性          pthread_mutex_lock(&mtx);          while (head == NULL)          {          //这个while要特别说明一下,单个pthread_cond_wait功能很完善,为何          //这里要有一个while (head == NULL)呢?因为pthread_cond_wait里的线          //程可能会被意外唤醒,如果这个时候head != NULL,则不是我们想要的情况。          //这个时候,应该让线程继续进入pthread_cond_wait          // pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx,          //然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立          //而被唤醒,唤醒后,该进程会先锁定先pthread_mutex_lock(&mtx);,再读取资源          //用这个流程是比较清楚的/*block-->unlock-->wait() return-->lock*/          pthread_cond_wait(&cond, &mtx);          p = head;          head = head->n_next;          printf("Got %d from front of queue/n", p->n_number);          free(p);           }           pthread_mutex_unlock(&mtx); //临界区数据操作完毕,释放互斥锁      }      pthread_cleanup_pop(0);      return 0; }