多线程编程原理简要分析

1.什么是线程:

线程是进程的一条执行路径。在UNIX系统下,线程常被称为轻量级的进程。同一主线程下的所有子线程都在同一进程空间运行,所以多个线程将共享进程中的全部系统资源,如虚拟地址空间,文件描述符等;但同一进程下的各个线程都有其各自的调用栈(call stack),寄存器环境(register context)和线程本地存储(thread-local storage),一个进程可以有很多线程,不同线程执行不同的任务。线程可以提高在多核环境下处理文件I/O或socket I/O等会产生阻塞情况的表现性能。如果采用多进程实现,创建进程需要的时间片比线程大,并且进程间的通信需要在用户空间和内核空间进行频繁的切换,花销很大;而线程间的通信可以使用共享的全局变量,因此十分高效。
进程创建后,会缺省的生成一个线程,该缺省的线程称为主线程,也称为控制线程,在C/C++中,主线程是通过main函数进入的线程,在主线程中,调用pthread_create创建子线程。每个线程都有其自身的线程id,可以通过函数pthread_self()来获取自己的线程id。除主线程外,各个子线程之间的关系是对等的,主线程和子线程之间的默认的关系为,一旦主线程执行完毕退出,所有的子线程执行都将终止。这时整个进程结束或僵死,部分线程保持一种终止执行但还未销毁的状态,而进程必须在其所有线程销毁后销毁,这时进程处于僵死状态。线程函数执行完毕退出,或以其他非常方式终止,线程进入终止态,但是为线程分配的系统资源不一定释放,可能在系统重启之前,一直都不能释放,终止态的线程,仍旧作为一个线程实体存在于操作系统中,什么时候销毁,取决于线程属性。在这种情况下,主线程和子线程通常定义以下两种关系:
1.可会合(joinable):主线程需要执行等待操作,在子线程结束后,主线程的等待操作执行完毕,主线程和子线程会合,之后主线程继续执行等待操作之后的下一步操作。主线程需要会合所有可会合的子线程,在主线程的线程函数内部调用子线程对象的wait函数实现,即使子线程能够在主线程之前执行完毕,进入终止态,也必须执行会合操作,否则,系统不会主动销毁线程,分配给该线程的系统资源也不会被释放。

2.相分离(detached):表示子线程无需和主线程会合,也就是相分离的,这种方式常用在线程数较多的情况下,有时让主线程逐个等待子线程结束,或者让主线程安排每个子线程结束的等待顺序,是很困难或不可能的,所以在并发子线程较多的情况下,这种方式也会经常使用。
线程的分离状态决定一个线程以什么样的方式来终止自己,在默认的情况下,线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束,只有当pthread_join函数返回时,创建的线程才算终止,释放自己占用的系统资源,而分离线程没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。

通过调用函数pthread_create可以创建一个子线程,函数原型及参数含义如下:

#include <pthread.h>

       int pthread_create(pthread_t *thread, const pthread_attr_t *attr,  void *(*start_routine) (void *), void *arg);

       Compile and link with -pthread.
       DESCRIPTION
       The  pthread_create()  function  starts a new thread in the calling process.  The new
       thread starts execution by invoking start_routine(); arg is passed as the sole  argu‐
       ment of start_routine().
       
 The new thread terminates in one of the following ways:

       * It  calls  pthread_exit(3),  specifying  an  exit status value that is available to
         another thread in the same process that calls pthread_join(3).

       * It returns from start_routine().  This is  equivalent  to  calling  pthread_exit(3)
         with the value supplied in the return statement.

       * It is canceled (see pthread_cancel(3)).

       * Any  of  the  threads  in  the process calls exit(3), or the main thread performs a
         return from main().  This causes the termination of all threads in the process.

The attr argument points to a pthread_attr_t structure whose  contents  are  used  at
       thread  creation  time  to determine attributes for the new thread; this structure is
       initialized using pthread_attr_init(3) and related functions.  If attr is NULL,  then
       the thread is created with default attributes.
       Before  returning,  a  successful  call  to pthread_create() stores the ID of the new
       thread in the buffer pointed to by thread; this identifier is used to  refer  to  the
       thread in subsequent calls to other pthreads functions.
RETURN VALUE
       On success, pthread_create() returns 0; on error, it returns an error number, and the
       contents of *thread are undefined.

typedef struct
{
int detachstate; 线程的分离状态
int schedpolicy; 线程调度策略
struct sched_param schedparam; 线程的调度参数
int inheritsched; 线程的继承性
int scope; 线程的作用域
size_t guardsize; 线程栈末尾的警戒缓冲区大小
int stackaddr_set;
void * stackaddr; 线程栈的位置
size_t stacksize; 线程栈的大小
}pthread_attr_t;

线程创建后,默认的属性是joinable(可会合)的,在该状态下,主线程需要调用pthread_join等待子线程退出,否则会导致子线程占有的资源无法释放,造成内存泄漏等问题,所以在创建一个线程是,可以将创建的线程属性设置为相分离状态,这样该子线程执行完毕后进入终止态,然后就释放其占有的资源。具体的将线程设置为相分离状态的方法有:

  1. 线程里面调用 pthread_detach(pthread_self()) 这个方法最简单。
  2. 在创建线程的属性设置里设置PTHREAD_CREATE_DETACHED属性。

2.互斥锁:

在多线程程序中,由于同一进程下的多个线程都可以访问进程所分配的资源,因此,在进程中定义的变量就可以被多个线程访问和修改,试想,当多个线程访问同一个共享变量后,每个线程对该共享变量的修改都是不同的,这会导致该共享变量的值合我们希望等到的值有所不同,因为在我们修改该共享变量的同时,其他的线程也会来修改该共享变量。因此,在多线程程序中,访问共享变量时就需要制定一定的规则,常用的方法有互斥锁,具体方法就是,当某个线程访问该共享变量时,该线程先需要设置与该共享资源相关的互斥锁,如果该互斥锁被其他线程使用了,该线程当然就不能获得该共享资源。当该线程拿到该互斥锁后,就可以访问该共享变量,这时其他线程就不能在设置互斥锁,当该线程使用完该共享变量之后,则需要将该互斥锁释放,如果释放互斥量时有一个一上的线程阻塞,那么该锁上的阻塞线程都会变成可运行状态,第一个变为运行的线程就可以设置该互斥锁,别的线程则继续等待直到该互斥锁重新变为可用。通过互斥锁,有效的解决了多线程程序中多个线程对共享资源的访问时数据不一致的问题,其本质就是让各个线程对共享资源的访问是有序的,而实现的方法就是通过互斥锁。
需要注意的是,使用到互斥锁时,需要使用函数pthread_mutex_init来初始化一个互斥锁,在互斥锁使用完成后,需要使用函数pthread_mutex_destroy将互斥锁摧毁。

3.死锁

如果两个线程在相互请求另一个线程拥有的共享资源,那么这两个线程都无法向前运行,于是就会产生死锁。另外,如果线程试图对互斥量加锁两次,也会导致该线程自身陷入死锁状态。
产生死锁的必要条件:
1.互斥
2.占有且等待
3.不可抢占
4.循环等待,即存在一个线程链,使得每个线程都拥有下一个线程运行所需要的必要的资源。
要避免死锁,我们可通过打破线程死锁产生的必要条件中的后三个条件。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值