一.线程分离:

  1. 概述:

  在任何一一个时间点上,线程是可结合的(joinable)或者是分离的(detached)。一一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是不释放的。相反,一一个分离的线程是不能被其他线程回收或杀死的,它的存储器 资源在它终止止时由系统自自动释放。

  默认情况下,线程被创建成可结合的。为了避免存储器泄漏,每个可结合线程都应该要么被显示示地回收,即调用用pthread_join;要么通过调用用pthread_detach函数被分离。

PS:pthread_join函数是以阻塞式等待的,而调用pthread_detach函数则是非阻塞式的等待。

   2.相关函数:

pthread_detach函数:pthread_detach(pthread_t  thread)

返回值:成功返回0,失败返回一个错误号。



3.相关代码:

1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<pthread.h>
  4 
  5 void *create_thread(void *ptr)
  6 {
  7     pthread_detach(pthread_self());
  8     printf("%s",(char *)ptr);
  9     return NULL;
 10 }
 11 
 12 
 13 int main()
 14 {
 15     pthread_t tid;
 16     int err = pthread_create(&tid, NULL, create_thread, "thread is running....\n");
 17     if(err != 0)
 18     {
 19         printf("create thread failed,err info is: %s\n", strerror(err));
 20     }
 21 
 22     sleep(1);
 23     void *errno = NULL;
 24     int ret = 0;
 25     ret = pthread_join(tid, &errno);
 26 
 27     if(ret != 0)
 28     {
 29         printf("wait for thread failed, err info is: %d\n",(int)errno);
 30     }
 31     else
 32     {
 33         printf("wait for thread success\n");
 34     }
 35     return ret;
 36 }                                                                                                                                                              
~

PS:也可以在主线程中分离创建的线程。

执行结果:

wKioL1cXIzzQuR_mAAApVO_HUq4566.png




二.线程互斥:

  1. 概述:

   在多线程环境中,可能会发生多个线程同时(相对而言)访问共享数据,就可能会发生冲突。因为修改一个变量不是原子操作,它包括从内存中读取变量,改变寄存器中的值,把寄存器中的值写会内存三步,在其中任何一步都可能有其它线程对这个变量进行操作。所以在多线程中,一个线程要对临界区进行操作,则要使用线程互斥。

   解决线程互斥的办法是引入互斥锁(Mutex,MutualExclusive Lock),获得锁的线程可以完成“读-修改-写”的操作,然后释放锁给其它线程,没有获得锁的线程只能等待而而不能访问共享数据,这样“读-修改-写”三步操作组成一一个原子子操作,要么都执行行,要么都不执行行,不会执行行到中间被打断,也不会在其它处理器上并行行做这个操作。

   一般情况下,如果同一个线程先后两次调用lock,在第二次调用用时,由于锁已经被占用,该线程会挂起等待别的线程释放锁,然而锁正是被自己占用着的,该线程又被挂起而没有机会释放锁,因此 就永远处于挂起等待状态了,这叫做死锁(Deadlock)。另一种典型的死锁情形是这样:线程A获 得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放 锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都 永远处于挂起状态了。不难想象,如果涉及到更多的线程和更多的锁,死锁的问题将会 变得复杂和难以判断。
   写程序时应该尽量避免同时获得多个锁,如果一定有必要这么做,则有一个原则:如果所有线程在需要多个锁时都按相同的先后顺序(常见的是按Mutex变量的地址顺序)获得锁,则不会出现死 锁。比如一个程序中用到锁1、锁2、锁3,它们所对应的Mutex变量的地址是锁1<锁2<锁3,那么 所有线程在需要同时获得2个或3个锁时都应该按锁1、锁2、锁3的顺序获得。如果要为所有的锁确 定一个先后顺序比较困难,则应该尽量使用pthread_mutex_trylock调用用代替pthread_mutex_lock 调 用,以免死锁。


   2.相关函数:

(1).静态分配(全局变量/static变量):

pthread_mutex_t mutex =     PTHREAD_MUTEX_INITIALIZER

功能:定义一个全局的或静态的mutex。

(2).pthread_mutex_init函数:  int  pthread_mutex_init(pthread_mutex_t *restrict mutex, 

                                                                                                     const pthrad_mutexattr_t *restrict attr)

attr参数:对mutex进行初始化,一般为NULL,表示缺省属性。

返回值:成功返回0,失败返回错误号。

(3).pthread_mutex_destroy函数:  int pthread_mutex_destory(pthread_mutex_t *mutex)

函数功能:销毁一个mutex。

返回值:成功返回0,失败返回错误号。

(4).pthread_mutex_lock函数: int  pthread_mutex_lock(pthread_mutex_t  *mutex)

函数功能:获得一个锁。(阻塞式)

返回值:成功返回0,失败返回错误号。

(5).pthread_mutex_unlock函数:int  pthread_mutex_unlock(pthread_mutex_t  *mutex)

函数功能:释放一个锁。

返回值:成功返回0,失败返回错误号。

(6).pthread_mutex_trylock函数:int  pthread_mutex_trylock(pthread_mutex_t  *mutex)

函数功能:获得一个锁。(非阻塞式)

返回值:成功返回0,如果mutex已经被另一个线程获得,则会立即返回EBUSY。


PS:在哪叫互斥锁很关键,在一个适当的位置加锁能提高线程的共享性。因为加互斥锁就相当于一个线程独占了临界资源,但是线程强调共享,所以应当在关键的临界资源处加锁。



3.相关代码:

(1).没有加锁的情况:

 1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<pthread.h>
  4 
  5 const int COUNT = 5000;                                                                                                                                        
  6 static int g_count = 0;
  7 
  8 void* create_thread(void *ptr)
  9 {
 10     pthread_detach(pthread_self());
 11     int tem = 0;
 12     int i = 0;
 13     for(;i < COUNT; i++)
 14     {
 15         tem = g_count;
 16         printf("thread id is:%ul  g_count is:%d\n", (unsigned long)pthread_self(), g_count);
 17         g_count = tem + 1;
 18     }
 19     return NULL;
 20 }
 21 
 22 
 23 int main()
 24 {
 25     pthread_t tid1;
 26     pthread_t tid2;
 27     pthread_create(&tid1, NULL, create_thread, NULL);
 28     pthread_create(&tid2, NULL, create_thread, NULL);
 29     sleep(1);
 30 
 31     printf("end is:%d\n", g_count);
 32     return 0;
 33 }

执行结果:

wKioL1cXNVewUBpcAABKp-nPdTg675.png

                                                       图一

wKioL1cXNXew4H43AABCYWU1rwQ585.png

                                                          图二

如果按正常顺序依次调用线程1和线程2,最终结果应该是10000,但是从图一中可以看出线程进行了切换,两个线程同时访问了临界资源。


(2).加互斥锁的情况:

相关代码:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<pthread.h>
  4 
  5 const int COUNT = 5000;
  6 static int g_count = 0;
  7 pthread_mutex_t mutex_lock =  PTHREAD_MUTEX_INITIALIZER;
  8 
  9 void* create_thread(void *ptr)
 10 {
 11     pthread_detach(pthread_self());
 12     int tem = 0;
 13     int i = 0;
 14     for(;i < COUNT; i++)
 15     {
 16         pthread_mutex_lock(&mutex_lock);
 17         tem = g_count;
 18         printf("thread id is:%ul  g_count is:%d\n", (unsigned long)pthread_self(), g_count);
 19         g_count = tem + 1;
 20         pthread_mutex_unlock(&mutex_lock);
 21     }
 22     return NULL;
 23 }
 24 
 25 
 26 int main()
 27 {
 28     pthread_t tid1;
 29     pthread_t tid2;
 30     pthread_create(&tid1, NULL, create_thread, NULL);
 31     pthread_create(&tid2, NULL, create_thread, NULL);
 32     sleep(1);
 33 
 34     printf("end is:%d\n", g_count);
 35     pthread_mutex_destroy(&mutex_lock);
 36     return 0;
 37 }

执行结果:

wKioL1cXPGXS15VWAAA8DLEbrZw181.png

                                                    图一

wKioL1cXPIzS2gYDAAA93oEdJzA688.png

                                                     图二

wKiom1cXO_GSpXWRAAA2IGBcwhQ208.png

                                                    图三

从图一和图二中可以看到,两个线程是没有冲突访问临界资源的。