一.线程分离:
- 概述:
在任何一一个时间点上,线程是可结合的(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:也可以在主线程中分离创建的线程。
执行结果:
二.线程互斥:
- 概述:
在多线程环境中,可能会发生多个线程同时(相对而言)访问共享数据,就可能会发生冲突。因为修改一个变量不是原子操作,它包括从内存中读取变量,改变寄存器中的值,把寄存器中的值写会内存三步,在其中任何一步都可能有其它线程对这个变量进行操作。所以在多线程中,一个线程要对临界区进行操作,则要使用线程互斥。
解决线程互斥的办法是引入互斥锁(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 }
执行结果:
图一
图二
如果按正常顺序依次调用线程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 }
执行结果:
图一
图二
图三
从图一和图二中可以看到,两个线程是没有冲突访问临界资源的。