多线程编程1

来源:互联网 发布:知乎电子书 编辑:程序博客网 时间:2024/05/21 16:56

线程的概念:

我们知道,进程在各自独立的地址空间中运行,进程之间共享数据需要用mmap或者进程间通信机制,在一个进程的地址空间中执行多个控制流程的时候,就用到线程了,如:实现一个图像界面的下载软件,需要和用户交互,等待处理用户的鼠标键盘事件,又需要同时下载多个文件,等待和处理多个网络主机发来的数据,这些任务都需要一个“等待-处理”的循环,可以用多线程实现,一个线程专门负责与用户交互,另几个线程每个线程负责和一个网络主机通信。

main函数和信号处理函数是同一个进程地址空间中的多个控制流程,多线程也是如此,但是比信号处理函数更灵活,信号处理函数的控制流程只是在信号递达时产生,在处理完信号之后就结束,而多线程的控制流程可以长期并存,操作系统会在各个线程之间调度和切换,就像在多个进程之间调度和切换一样。由于同一进程的多个线程共享同一地址空间,因此如果一个函数,在各线程中都可以调用,如定义一个全局变量,在各线程中都可以访问到,除此之外还有以下环境:

1.文件描述符表 2.每种信号的处理方式3.当前工作目录4.用户id和组id

 有些资源是每个线程各有一份的:

1.线程id   2.上下文,包括各种寄存器的值,程序计数器和栈指针

3. 栈空间 4.errno变量5.信号屏蔽字6.调度优先级

线程库函数是有POSIX标准定义的,称为POSIX thread或者品thread。在Linux上线程函数位于libpthread共享库中,因此在编译时加上-lpthread选项。

 

 

 

线程的控制:

1.创建线程

创建线程调用的pyhread_creat()函数,成功返回0,失败返回错误号,但是以前学过的系统函数都是成功返回0,失败返回-1,而错误号保存在全局变量errno中,而pthread库的函数都是通过返回值返回错误号,虽然每个线程也都有一个errno,但是为了兼容其它函数接口而提供的,pthread库本身并不使用它,通过返回值返回错误码更加清晰。

如果任意一个线程调用exit_exit,则整个进程的所有想成都终止,由于从main函数return也相当于调用exit

 

2.终止线程

  终止某个线程而不肿着整个进程有三种方法:

  1.从线程函数return。这种方法对主线程不适用,从main哈数return相当于调用exit

  2.一个线程可以调用pthread_cancel终止同一进程中的另一进程。

  3. 线程可以调用pthread_exit终止自己。

 

   注意:pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能再线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

 

3.线程等待

     线程等待调用的是函数pthread_join,成功则返回0,失败范湖错误号。调用该函数的线程将挂起等待,直到idthread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的。

1.如果thread线程通过return返回,value_ptr所指向的单元存放的是thread线程函数的返回值。

2.如果thread线程被别的想成调用pthread_cancel异常终掉,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED

3.如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。如果对thread线程的终止状态不感兴趣,可以传给NULLvalue_ptr参数。

 

一般情况下,线程终止后,其终止状态一直保留到其它线程动用pthread_join获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL。对一个尚未detach的线程调用pthread_joinpthread_detach都可以把该线程置为detach状态,也就是说,不能对同一个线程调用俩次pthread_join,或者如果已经对一个线程调用了pthread_datach就不能再调用pthread_join了。成功返回0,失败返回错误号。

 

有关分离线程:

在任何一个时间点上,线程是可结合的或者是分离的。一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。默认情况下,线程被创建可结合的,为了避免存储器泄露,每个可结合线程都应该要么被显示地回收,即调用pthread_join;要么通过调用pthread_detach函数被分离。如果一个可结合线程结束运行但没有被join,则它的状态类似于进程中还有一部分资源没有被回收,所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出码,回收其资源。

由于调用pthread_join后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此。这时可以在子线程中加入代码pthread_detach(pthread_self())或者父进程调用ptjread_detach(thread_id)这将该子进程的状态设置为分离的,如此一来,该线程运行结束后会自动释放所有资源。

 

 

线程的同步与互斥

1.mutex(互斥量)

   多个线程同时访问共享数据时可能会冲突,这和信号的可重入性是同样问题。如:把俩个线程都要把某个全局变量增加1,这个操作在某平台需要三条指令完成:

1.从内存读变量值到寄存器

2.寄存器的值加1

3.将寄存器的值写回内存

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

线程的概念:

我们知道,进程在各自独立的地址空间中运行,进程之间共享数据需要用mmap或者进程间通信机制,在一个进程的地址空间中执行多个控制流程的时候,就用到线程了,如:实现一个图像界面的下载软件,需要和用户交互,等待处理用户的鼠标键盘事件,又需要同时下载多个文件,等待和处理多个网络主机发来的数据,这些任务都需要一个“等待-处理”的循环,可以用多线程实现,一个线程专门负责与用户交互,另几个线程每个线程负责和一个网络主机通信。

main函数和信号处理函数是同一个进程地址空间中的多个控制流程,多线程也是如此,但是比信号处理函数更灵活,信号处理函数的控制流程只是在信号递达时产生,在处理完信号之后就结束,而多线程的控制流程可以长期并存,操作系统会在各个线程之间调度和切换,就像在多个进程之间调度和切换一样。由于同一进程的多个线程共享同一地址空间,因此如果一个函数,在各线程中都可以调用,如定义一个全局变量,在各线程中都可以访问到,除此之外还有以下环境:

1.文件描述符表 2.每种信号的处理方式3.当前工作目录4.用户id和组id

 有些资源是每个线程各有一份的:

1.线程id   2.上下文,包括各种寄存器的值,程序计数器和栈指针

3. 栈空间 4.errno变量5.信号屏蔽字6.调度优先级

线程库函数是有POSIX标准定义的,称为POSIX thread或者品thread。在Linux上线程函数位于libpthread共享库中,因此在编译时加上-lpthread选项。

 

 

 

线程的控制:

1.创建线程

创建线程调用的pyhread_creat()函数,成功返回0,失败返回错误号,但是以前学过的系统函数都是成功返回0,失败返回-1,而错误号保存在全局变量errno中,而pthread库的函数都是通过返回值返回错误号,虽然每个线程也都有一个errno,但是为了兼容其它函数接口而提供的,pthread库本身并不使用它,通过返回值返回错误码更加清晰。

如果任意一个线程调用exit_exit,则整个进程的所有想成都终止,由于从main函数return也相当于调用exit

 

2.终止线程

  终止某个线程而不肿着整个进程有三种方法:

  1.从线程函数return。这种方法对主线程不适用,从main哈数return相当于调用exit

  2.一个线程可以调用pthread_cancel终止同一进程中的另一进程。

  3. 线程可以调用pthread_exit终止自己。

 

   注意:pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能再线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

 

3.线程等待

     线程等待调用的是函数pthread_join,成功则返回0,失败范湖错误号。调用该函数的线程将挂起等待,直到idthread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的。

1.如果thread线程通过return返回,value_ptr所指向的单元存放的是thread线程函数的返回值。

2.如果thread线程被别的想成调用pthread_cancel异常终掉,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED

3.如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。如果对thread线程的终止状态不感兴趣,可以传给NULLvalue_ptr参数。

 

一般情况下,线程终止后,其终止状态一直保留到其它线程动用pthread_join获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL。对一个尚未detach的线程调用pthread_joinpthread_detach都可以把该线程置为detach状态,也就是说,不能对同一个线程调用俩次pthread_join,或者如果已经对一个线程调用了pthread_datach就不能再调用pthread_join了。成功返回0,失败返回错误号。

 

有关分离线程:

在任何一个时间点上,线程是可结合的或者是分离的。一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。默认情况下,线程被创建可结合的,为了避免存储器泄露,每个可结合线程都应该要么被显示地回收,即调用pthread_join;要么通过调用pthread_detach函数被分离。如果一个可结合线程结束运行但没有被join,则它的状态类似于进程中还有一部分资源没有被回收,所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出码,回收其资源。

由于调用pthread_join后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此。这时可以在子线程中加入代码pthread_detach(pthread_self())或者父进程调用ptjread_detach(thread_id)这将该子进程的状态设置为分离的,如此一来,该线程运行结束后会自动释放所有资源。

 

 

线程的同步与互斥

1.mutex(互斥量)

   多个线程同时访问共享数据时可能会冲突,这和信号的可重入性是同样问题。如:把俩个线程都要把某个全局变量增加1,这个操作在某平台需要三条指令完成:

1.从内存读变量值到寄存器

2.寄存器的值加1

3.将寄存器的值写回内存

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

原创粉丝点击