多线程

POSIX:

 

POSIX 表示可移植操作系统接口(Portable Operating System Interface ,缩写为 POSIX 是为了读音更像 UNIX)。电气和电子工程师协会(Institute of Electrical and Electronics EngineersIEEE)最初开发 POSIX 标准,是为了提高 UNIX 环境下应用程序的可移植性。由ANSIISO标准化。

 

POSIX的诞生和Unix的发展是密不可分的,

Unix70年代诞生于贝尔实验室,并于80年代向美各大高校分发V7版的源码以做研究。加利福尼亚大学伯克利分校在V7的基础上开发了BSD Unix。后来很多商业厂家意识到Unix的价值也纷纷以贝尔实验室的System VBSD为基础来开发自己的Unix

 

但由于各厂家对Unix的开发各自为政,造成Unix的版本相当混乱,给软件的可移植性带来很大的困难,对Unix的发展极为不利,为结束这种局面,IEEE开发了POSIX,POSIX在源代码级别上定义了一组最小的Unix(Unix)操作系统接口,然而,POSIX并不局限于UNIX,许多其他的操作系统egMicrosoft windows NT  Linux等都支持(部分)POSIX标准。

POSIX 已然发展成为一个非常庞大的标准族,某些部分正处在开发过程中。

 

//---------------------------------

 

Pthread线程库:

POSIX 1003.1c定义了处理线程的一系列C语言类型的API函数,提供了一个可移植的多线程库,

称为Pthreads.

Pthreads现在已成为Linux操作系统中多线程接口的标准,并且广泛使用在大多数的unix平台上。针对windows操作系统,Pthreads也存在一个开放源代码的版本,称为pthreads-win32

 

可以通过_POSIX_THREADS宏测试系统是否支持Pthread#ifdef _POSIX_THREADS

Pthreads库的函数原型定义在<pthread.h>中,在编写多线程程序时需要include该头文件

编译时需要连接libpthread库。

 

 

Pthread线程库提供的函数一般都已pthread 开头,eg

前缀                  功能集合

pthread_ 线程或子线程

pthread_attr_ 线程对象属性

pthread_mutex_ 互斥量

pthread_mutexattr 互斥量对象属性

pthread_cond_ 条件变量

pthread_condattr 条件变量属性

 

数据类型:

pthread_t:线程句柄

pthread_attr_t:线程属性

 

操作函数:

pthread_create():创建一个线程

pthread_exit():终止当前线程

pthread_cancel():中断另外一个线程的运行

pthread_join():阻塞当前的线程,直到另外一个线程运行结束

pthread_attr_init():初始化线程的属性

pthread_attr_setdetachstate():设置脱离状态的属性(决定这个线程在终止时是否可以被结合)

pthread_attr_getdetachstate():获取脱离状态的属性

pthread_attr_destroy():删除线程的属性

pthread_kill():向线程发送一个信号

 

同步函数:

用于 mutex 和条件变量

pthread_mutex_init() 初始化互斥锁

pthread_mutex_destroy() 删除互斥锁

pthread_mutex_lock():占有互斥锁(阻塞操作)

pthread_mutex_trylock():试图占有互斥锁(不阻塞操作)。即,当互斥锁空闲时,将占有该锁;否则,立即返回。

pthread_mutex_unlock(): 释放互斥锁

pthread_cond_init():初始化条件变量

pthread_cond_destroy():销毁条件变量

pthread_cond_signal(): 唤醒第一个调用pthread_cond_wait()而进入睡眠的线程

pthread_cond_wait(): 等待条件变量的特殊条件发生

Thread-local storage(或者以Pthreads术语,称作线程特有数据):

pthread_key_create(): 分配用于标识进程中线程特定数据的键

pthread_setspecific(): 为指定线程特定数据键设置线程特定绑定

pthread_getspecific(): 获取调用线程的键绑定,并将该绑定存储在 value 指向的位置中

pthread_key_delete(): 销毁现有线程特定数据键

 

 

工具函数:

pthread_equal(): 对两个线程的线程标识号进行比较

pthread_detach(): 分离线程

pthread_self(): 查询线程自身线程标识号\

 

 

 

 

 

 

 

//---------------------------------

 

#include <pthread.h>

 

  pthread_t tid[NUMT];

  

    for(i=0; i< NUMT; i++) {

    error = pthread_create(&tid[i],

                           NULL, /* default attributes please */

                           pull_one_url,

           for(i=0; i< NUMT; i++) {

    error = pthread_join(tid[i], NULL);

(void *)urls[i]);

  

  

  

  

  

 //---------------------------------

  线程创建

函数原型:int pthread_create(pthread_t*restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void),void *restrict arg);

返回值:若是成功建立线程返回0,否则返回错误的编号。

形式参数:pthread_t*restrict tidp要创建的线程的线程id指针;const pthread_attr_t *restrict attr创建线程时的线程属性;void* (start_rtn)(void)返回值是void类型的指针函数;void *restrict arg start_rtn的形参。

线程挂起:该函数的作用使得当前线程挂起,等待另一个线程返回才继续执行。也就是说当程序运行到这个地方时,程序会先停止,然后等线程idthread的这个线程返回,然后程序才会断续执行。

函数原型:intpthread_join(pthread_tthread, void **value_ptr);

参数说明如下:thread等待退出线程的线程号;value_ptr退出线程的返回值。

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

线程退出

函数原型:voidpthread_exit(void *rval_ptr);

获取当前线程id

函数原型:pthread_tpthread_self(void);

互斥锁

创建pthread_mutex_init;销毁pthread_mutex_destroy;加锁pthread_mutex_lock;解锁pthread_mutex_unlock

条件锁

创建pthread_cond_init;销毁pthread_cond_destroy;触发pthread_cond_signal;广播pthread_cond_broadcast;等待pthread_cond_wait

  

 

 

 

//---------------------------------

线程安全: 指代码能够被多个线程调用而不会产生错误的结果,大部分现行函数

可以利用Pthreads提供的工具------互斥量,条件变量和线程私有数据,实现线程的安全。

 

同步可以通过很多种方式实现,其中最常见的是互斥量,条件变量,信号量和事件,还可以通过消息传递机制,如UNIX管道,SOCKET POSIX消息队列,或者是其他异步进程间(本地或者网络)的通信协议。任何通信协议里都包含某种相同的同步机制,因为如果没有同步,则传送数据将导致混乱而不是通信。

 

线程,互斥量和条件变量时本书的主要话题,线程是计算机中的可执行单元,互斥量阻止线程间发生不可预期的冲突,一旦避免了冲突,条件变量让线程等待直到可以安全地执行,互斥量和条件变量都是用来同步线程间操作的。

 

  线程就是进程里足以执行代码的部分,在大多数计算机系统中,这意味着线程应包括以下内容:当前指令位置指针(通常称为计数器或PC),栈顶指针(SP),通用寄存器,浮点或地址寄存器,线程可能还包括像处理器状态和协处理器寄存器等数据,线程不包括进程中的其他数据,如地址空间和文件描述符,一个进程中的所有线程共享文件和内存空间,包括程序文本段和数据段。

 

//---------------------------------------------

 

程序中使用线程标示符ID来表示线程,线程ID属于封装的pthread_t类型

 

Pthread线程通过调用你提供的某些函数开始,这个”线程函数”应该只有一个void*类型参数

并返回一个void*类型参数。通过向pthread_create()函数传送线程函数地址和线程函数调用的参数来创建线程。

 

当创建线程时,pthread_create函数返回一个pthread_t类型的线程ID,并保存在thread参数中,通过这个线程ID,程序可以引用该线程,线程可以通过调用pthread_self来获得一个线程ID,要对线程进行任何操作都必须通过线程ID

 

Pthread_equal()函数来比较两个线程ID,只能比较二者是否相同。相等则Pthread_equal()函数返回非零值,否则返回零值。

 

 

分离线程: 意味着通知系统不再需要此线程,允许系统将分配给它的资源回收。

Pthread_detach()函数类分离它,线程可以分离自己,任何获知其ID的其他线程也可以随时分离它。

所以为了确保终止线程的资源还可以被进程使用,应该在每个线程结束时分离它们,一个没有被分离的线程终端会保留其虚拟内存,包括它们的堆栈和其他系统资源。

 

如果需要获得线程的返回值,或者需要获得线程的返回值,或者需要获知其何时结束,应该调用pthread_join函数,

 

//---------------------------------------------

 

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#include <windows.h>

#include "errors.h"

 

//thread start routine

void* thread_routine(void *arg)

{

return arg;

}

 

int main( int argc ,char *argv[])

{

pthread_t thread_id;

void *thread_result;

int status;

status = pthread_create(&thread_id, NULL,thread_routine,NULL);

if (status != 0)

{

printf("%d,%s",status,"create thread");

}

status = pthread_join(thread_id,&thread_result);

if (status != 0)

{

printf("%d,%s",status,"join thread");

}

if (thread_result == NULL)

{

return 0;

}

else

return 1;

}

 

 

 

创建线程:

进程的初始线程随着进程的创建而创建,main()函数。在一个完全支持线程编程的系统中,可能没有代码能够脱离线程运行。

其他线程通过显示的函数调用建立,Pthread系统中建立线程的主要方式是调用pthread_create()

 

 

 

 

线程启动:

一旦线程被创建,最终它将开始执行机器指令,初始指令序列将导致在pthread_create()调用中指定的线程启动函数的执行。线程启动函数的运行参数也是在创建线程是指定,新线程使用参数NULL开始运行thread_routine函数中的用户代码。

 

初始线程中,线程的启动函数(main函数)是从程序外部被调用的,例如,很多UNIX系统中将程序链接到一个crt0.o文件上,该文件负责初始化进程,然后调用main函数,这是一个较小的实现区别,还有其他的区别,如,main函数的调用参数(argcargv)与普通线程的启动函数不同(void*参数),另外,如果普通线程从启动函数中返回,则线程终止而其他线程依然可以运行,但初始进程从main函数中返回时,进程终止(进程内所有线程也被终止)。如果你希望在初始线程终止时,进程中的其他线程继续执行,则需要在初始线程中调用pthread_exit()而不是从main函数中返回。

另一个重要的区别是:在大多数系统中,初始线程运行在默认进程堆栈上,该堆栈可以增长到足够的尺寸,而在某些实现中,普通线程的堆栈空间是受限的。

 

 

 

运行和阻塞:

 

Pthread_join()

 

 

 

 

终止:

线程通常从启动函数中返回来终止自己

Pthread_exit()退出线程

Pthread_cancel()取消线程

线程在调用完每个清理过程后将进入终止态,如果该线程没有被分离,它所有的资源就没有被回收,它还可以被其他线程调用pthread_join连接,这时进程被称为“僵”线程,因为即使已经死了 但还存在,僵线程可能会保留其运行时的大部分甚至所有资源,因此不应该让线程长时间处于这种状态。如果处于终止态,且线程已经被分离,则它立刻进入下一节----------回收。

 

 

Pthread_cleanup_push()

Pthread_cleanup_pop()

 

回收:

如果使用detachstate属性(设为PTHREAD_CREATE_DETACH)建立线程,或者调用pthread_detach分离线程,则当线程结束时将被立刻回收。

如果终止线程没有被分离,则它将一直处于终止态直到被分离(通过pthread_detach)或者被连接(通过pthread_join).。线程一旦被分离,就不能再访问它了。

回收将释放所有在线程终止时未释放的系统和进程资源,包括存线程返回值的内存空间,堆栈,保存寄存器状态的内存空间等。其中一些资源可能已在线程终止时被释放,但必须记住:在线程终止后上述资源就不该被访问了,一旦线程被回收,线程ID就无效了,不能再连接它,取消它或者执行其他任何操作。

 

 

 

 

 

//--------------------------------------------

 

同步

 

使线程同步最通用和常用的方法就是确保对相同(或相关)数据的内存访问“互斥地”进行,即一次只能允许一个线程写数据,其他线程必须等待。Pthread使用了一种特殊形式的信号灯--------互斥量

 

经验表明,正确使用互斥量比使用像通用信号灯之类的其他同步模型要容易,还能很容易地使用互斥量与条件变量结合建立任何同步模型,互斥量简单,灵活,并能被有效实现。

 

 

 

Pthread_mutes_trylock() 函数试图加锁互斥量。

 

创建和销毁互斥量

Pthread_mutes_t  mutex = PTHREAD_MUTEX_INITIALIZER;

Int pthread_nutex_init( pthread_mutex_t* mutex, pthread_mutexattr_t *attr);

Int pthread_mutex_destroy( pthread_mutex_t*mutex);

 

 程序中的互斥量是用 pthread_mutex_t 类型的变量来表示的,不能拷贝互斥量变量可以拷贝互斥量指针,这样就可以使多个函数或线程共享互斥量来实现同步。

使用宏  PTHREAD_MUTEX_INITIALIZER 来初始化声明具有默认属性的静态互斥量。

 

#include <pthread.h>

typedef struct my_struct_tag{

pthread_mutex_t  mutex;

int  value;

}my_struct_t;

 

my_struct_t data = {PTHREAD_MUTEX_INITIALIZER,0};

int main(int argc,char*argv[])

{

return 0;

}

 

 

通常不能静态地初始化一个互斥量,例如当使用malloc动态分配一个包含互斥量的数据结构时,这时,应该使用pthread_mutex_init()调用来动态地初始化互斥量,也可以动态地初始化静态声明的互斥量,但必须保证每个互斥量在使用前被初始化,而且只被初始化一次。

也可以在创建任何线程之前初始化它,如通过调用pthread_once()。如果需要初始化一个非缺省属性的互斥量,必须使用动态初始化。

如下程序:

 

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#include <windows.h>

 

typedef struct my_struct_tag{

pthread_mutex_t  mutex;

int  value;

}my_struct_t;

 

int main(int argc,char*argv[])

{

my_struct_t * data;

int status;

data = malloc(sizeof(my_struct_t));

if (data == NULL)

printf("%s","Allocate structure");

 

status = pthread_mutex_init(&data->mutex,NULL);

if (status != 0)

printf("%d,%s",status,"init mutex");

 

status = pthread_mutex_destroy(&data->mutex);

if(status != 0)

printf("%d,%s",status,"Destroy mutex");

 

(void)free(data);

 

return 0;

}

将互斥量与它要保护的数据明显地联系起来是个不错的主意,如果可以的话,将互斥量和数据定义在一起,

通过pthread_mutex_init调用动态初始化的互斥量时,应该调用pthread_mutex_destroy()来释放它,不需要释放一个使用PTHREAD_MUTEX_INITIALIZER宏静态初始化的互斥量。

 

 

加锁和解锁互斥量

Int pthread_mutex_lock( pthread_mutex_t * mutex);

Int pthread_mutex_trylock( pthread_mutex_t * mutex);

Int pthread_mutex_unlock( pthread_mutex_t * mutex);

通过调用 pthread_mutex_lockpthread_mutex_trylock锁住互斥量,处理共享数据,然后调用pthread_mutex_unlock解锁互斥量,为确保线程能够读取一组变量的一致的值,需要在任何读写这些变量的代码段周围锁住互斥量。

当调用线程已经锁住互斥量之后,就不能再加锁该互斥量,也不能解锁一个已经解锁的互斥量,也不能解锁有其他线程锁住的互斥量。

 

 

 

创建和释放条件变量:

Pthread_cond_t cod = PTHREAD_COND_INITIALIZER;

Int pthread_cond_init(pthread_cond_t * cond,  pthread_condattr_t *condattr);

Int pthread_cond_destroy(pthread_cond_t * cond);

 

程序中由pthread_cond_t类型的变量来表示条件变量,永远不要拷贝条件变量。

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#include <windows.h>

 

typedef struct my_struct_tag{

pthread_mutex_t  mutex;

pthread_cond_t   cond;

int  value;

}my_struct_t;

 

my_struct_t data = {PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER

,0}

 

int main(int argc,char*argv[])

{

 

return 0;

}

 

 

有时无法静态地初始化一个条件变量,例如,当使用malloc分配一个包含条件变量的结构时,这时,你需要调用pthread_cond_init()来动态初始化条件变量,还可以动态初始化静态声明的条件变量,但是必须确保每个条件变量在使用之前初始化且仅初始化一次,你可以在建立任何线程前初始化它,或者使用pthread_once()

 

 

 

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#include <windows.h>

 

typedef struct my_struct_tag{

pthread_mutex_t  mutex;

pthread_cond_t   cond;

int  value;

}my_struct_t;

 

my_struct_t data = {PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER

,0}

 

int main(int argc,char*argv[])

{

my_struct_t * data;

int status;

data = malloc(sizeof(my_struct_t));

if(data == NULL)

printf("%s","Allocate structure");

status = pthread_mutex_init(&data->mutex,NULL);

if(status != 0)

printf("%s","init mutex");

status = pthread_cond_init(&data->cond,NULL);

if (status != 0)

printf("%s","init condition");

 

status = pthread_cond_destroy(&data->cond);

if(status != 0)

printf("%s","destroy condition");

 

status = pthread_mutex_destroy(&data->mutex);

if(status != 0)

printf("%s","destroy mutex");

(void)free(data);

 

return 0;

}

 

当动态初始化条件变量时,应该在不需要它时调用pthread_cond_destroy()来释放它,不必释放一个通过PTHREAD_COND_INITIALIZER宏静态初始化的条件变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值