线程分离与线程互斥





一.线程分离:

  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:也可以在主线程中分离创建的线程。

执行结果:




二.线程互斥:

  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 }

执行结果:

图一

图二

如果按正常顺序依次调用线程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 }

执行结果:

图一

图二

图三

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

时间: 2024-11-03 22:03:51

线程分离与线程互斥的相关文章

Linux之线程、线程控制、线程属性

一.整体大纲 二.线程相关 1. 什么是线程    LWP:light weight process 轻量级的进程,本质仍是进程(在Linux环境下) 进程:独立地址空间,拥有PCB 线程:也有PCB,但没有独立的地址空间(共享) 区别:在于是否共享地址空间. 独居(进程):合租(线程). Linux下: 线程:最小的执行单位 进程:最小分配资源单位,可看成是只有一个线程的进程. 2. Linux内核线程实现原理     (1)线程实现原理 类Unix系统中,早期是没有“线程”概念的,80年代才

线程的同步与互斥,死锁

线程的同步与互斥 多个线程同时访问共享数据时可能会发生冲突,比如两个线程同时把一个全局变量加1,结果可能不是我们所期待的: 我们看这段代码的执行结果: #include <stdio.h> #include <stdlib.h> #include <pthread.h> static int g_count=0; void *thread(void *arg) { int index=0; int tmp=0; while(index++<5000) { tmp=

线程同步机制之互斥锁通信机制

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <string.h> void *thread_function(void *arg); pthread_mutex_t work_mutex; #define WORK_SIZE 1024 char work_area[WORK_SIZE]; int time_to_exit=0; int main(int argc,

线程的同步与互斥(死锁的产生和避免)

可以知道,一条语句对一个变量进行+1操作,转成汇编指令共有三条:将这个变量从内存中取出:将其值加1:再将加后的结果放回内存:当一个进程中的两个线程同时进行这个操作时,本来期望的是将变量进行两次加1,但中途有可能当一个线程刚从内存中将变量取出就被切换暂停了,此时线程会保存硬件上下文,第二个线程将变量加1之后前面切出去的线程回来继续执行,这时保存的还是变量原来的值,再将变量加1,会发现变量的最终结果并没有加2而是只加了1,因此这种操作并不是原子的. -------------------------

线程分离状态

分离状态的线程, 不能用pthread_join()等待它的终止状态. 例1:  修改属性, 创建分离状态线程 mypthread_attr_detach.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <pthread.h> //gcc -g mypthread_attr_detach.c -o mypthread_

linux线程控制&amp;线程分离

线程概念 线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元. 线程是程序中一个单一的顺序控制流程.进程内一个相对独立的.可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位.在单个程序中同时运行多个线程完成不同的工作,称为多线程. 线程资源 由于一个程序中的多个线程共享同一地址空间,因此代码段,数据段内容是共享的.除此之外,以下内容也是共享的: 1. 文件描述符表2. 每种信号的处理方式(SIG_IGN.SIG_DFL

线程同步:何时互斥锁不够,还需要条件变量?

http://www.blogjava.net/fhtdy2004/archive/2009/07/05/285519.html 线程同步:何时互斥锁不够,还需要条件变量? 很显然,pthread中的条件变量与Java中的wait,notify类似 假设有共享的资源sum,与之相关联的mutex 是lock_s.假设每个线程对sum的操作很简单的,与sum的状态无关,比如只是sum++.那么只用mutex足够了.程序员只要确保每个线程操作前,取得lock,然后sum++,再unlock即可.每个

线程概念及线程的同步与互斥

线程概念:它是运行在进程内部的的一个基本执行流,多线程的控制流程可以长期并存,一个进程中的数据段和代码段都是被该进程中的多个线程共享的,若定义一个函数,每个线程都可以调用,若定义一个全局变量,每个线程都可以访问. 线程还共享进程的以下内容:1.文件描述符表 2.当前的工作目录 3.用户id(uid)和组id(gid) 4.每种信号的处理方式. 但每个线程还必须有自己的私有部分:1.线程id 2.硬件上下文(硬件寄存器的值,栈指针等) 3.自己的栈空间(运行时的临时数据都要保存在自己的栈空间上)

线程分离

线程分离的作用:当主线程与新线程无关时,主线程无需等待新线程的结束. 1.进程属性初始化 pthread_attr_t pth_attr; pthread_attr_init(&pth_attr);2.进程分离属性设置. pthread_attr_setdetachstate(&pth_attr,PTHREAD_CREATE_DETACHED); 3.进程创建. 4.进程属性资源回收. pthread_attr_destroy(&pth_attr); #include<std