天天看点

linux pthread 多线程操作

简要说明一下进程和线程的区别,以及为何有了进程还需要线程。

一句话概括,进程是系统资源分配的最小单位,线程是CPU调度的最小单位。一个进程可以有多个线程,每个线程有自己的堆栈和寄存器,程序计数器等,它们共享所属进程的地址空间和其所占的其它资源(如全局变量,打开的文件等)

为何有了进程还要有线程。主要是以下两点:

1  更轻量

进程的创建和切换所花费的资源太大,系统必须为创建的进程分配独立的地址空间,建立各种数据表来维护它的堆栈段,代码段和数据段。相对而言,线程就简单多了,它使用进程的地址空间,并共享该进程大部分的资源。

2  通信更简单

进程间的通信方式不仅麻烦,而且耗费系统资源,而线程间的通信则简单许多, 因为他们共享进程的数据空间,我们只要控制好对这些临界资源的访问,就能轻易的实现线程间的通信。

下面介绍Posix多线程操作,主要介绍如下几个函数,最后以一个小例子来结尾:

#include<pthread.h>

int pthread_create(pthread_t*  pid, pthread_attr_t* attr, void* (*func)(void*),void* param)

创建一个线程,成功返回0,失败返回各种错误码

pid是线程创建成功的线程id

attr是线程属性,可以指定线程的优先级,堆栈的大小等等,通常为NULL

func是线程启动后执行的函数

param是执行函数的参数,若想要传递多个数据,则传入一个结构体

int pthread_exit(void* status)

调用后,该线程结束,可以设置返回值status,它将会被等待它的线程接收到(即调用pthread_join()等待该线程结束的线程)

int pthread_self()

返回本线程的pid

int pthread_yield()

成功返回0,失败返回各种错误码,该函数会让线程放弃CPU的执行,并把该线程放到它的静态优先级运行队列的末尾且其它线程被调度运行

int pthread_detach(pthread_t pid)

用于将指定pid的线程设置为脱离状态,那么线程在执行结束返回后,会自动释放所有资源。否则线程结束后会成为僵尸线程,需要等待别的线程对其使用pthread_join(),才会释放掉所有的资源

int pthread_join(pthread_t pid, void** status)

用于等待指定线程的结束,status用于接收参数(即pthread_exit里传递过来的,没有什么需求时置为NULL即可)

关于线程操作的函数就介绍到这里,但是上面已经提到了,多线程编程中,必须要控制好对临界资源的访问。下面介绍pthread_mutex和pthread_cond,互斥锁和条件变量,它们分别用来实现对临界资源的访问和线程同步。

先说说pthread_mutex互斥锁。

它有几个常用的函数

pthread_mutex_init(pthread_mutex_t* mutex,pthread_mutexattr_t* attr)

初始化一个互斥锁,第二个参数一般为NULL

pthread_mutex_lock(pthread_mutex_t* mutex)

对传入的互斥锁进行上锁操作,如果该互斥锁已经被占用,那么该函数会一直阻塞到获取到该互斥锁并给它上锁为止

pthread_mutex_unlock(pthread_mutex_t* mutex)

对传入的互斥锁进行解锁操作,与lock配套使用,用于完成对临界资源的访问

pthread_mutex_destroy(pthread_mutex_t* mutex)

销毁掉传入的互斥锁,释放其资源

条件变量的几个常用函数,条件变量一般都是和互斥锁配合使用来实现同步的。

pthread_cond_init(pthread_cond_t* cond,pthread_condattr_t* attr)

初始化操作,第二个参数通常为NULL

pthread_cond_wait(pthread_cond_t* cond, pthread_cond_mutex);

会使线程阻塞,直到等待条件变为真,该函数第二个参数是互斥锁变量,通常调用它之前都会给传入的mutex上锁,然后访问临界资源,调用该函数后,将会释放该互斥锁,

当被唤醒时,又会再次给该互斥锁变量上锁。这里得注意别造成死锁了。(另外该变量的作用也是在多个线程等待cond变量时,实现对cond变量访问的互斥)

pthread_cond_signal(pthread_cond_t* cond)

唤醒等待cond条件的一个线程

pthread_cond_broadcast(pthread_cond_t* cond)

唤醒等待cond条件的所有线程

pthread_cond_destory(pthread_cond_t* cond)

销毁条件变量

基本操作已经完毕了,下面以一个小例子来收尾(此处为了介绍条件变量和互斥量的使用,强行使用了它们)

1 主线程创建线程1

2 主线程创建线程2

3 线程1打印“this is pthread1 pid :”+pid

4 线程2打印"this is pthread2 pid :"+pid

5 线程1必须等到线程2结束后才能返回

6 主线程必须等到2个子线程都结束后才能返回

程序源代码:

#include <pthread.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

pthread_mutex_t mutex;

pthread_cond_t cond;

int count=0;

void* thread1(void* arg);

void* thread2(void* arg);

int main(int arg1, char** arg2){

pthread_t pid1,pid2;

pthread_mutex_init(&mutex,NULL);

pthread_cond_init(&cond,NULL);

pthread_create(&pid2,NULL,thread2,NULL);

pthread_create(&pid1,NULL,thread1,(void*)&pid2);

pthread_mutex_lock(&mutex);

while(count<2)

pthread_cond_wait(&cond,&mutex);

pthread_mutex_unlock(&mutex);

printf("this is main thread pid %d\n",(unsigned int)pthread_self());

pthread_mutex_destroy(&mutex);

pthread_cond_destroy(&cond);

return 0;

}

void* thread1(void* arg){

printf("this is pthread1 pid %d\n",(unsigned int)pthread_self());

pthread_mutex_lock(&mutex);

count++;

pthread_mutex_unlock(&mutex);

pthread_join(*((pthread_t*)arg),NULL);

pthread_cond_signal(&cond);

return 0;

}

void* thread2(void* arg){

printf("this is pthread2 pid %d\n",(unsigned int)pthread_self());

pthread_mutex_lock(&mutex);

count++;

pthread_mutex_unlock(&mutex);

pthread_exit(0);

}

这里稍微提一下,在pthread1里调用pthread_join时,我之前是将传递进来的arg强制转换成了 unsigned int,导致出现了coredump的错误,出现这种错误的原因是:

一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时就很容易因为bus error而core dump。

这里有一篇关于core dump文章,有兴趣的朋友可以去看看

http://blog.csdn.net/tenfyguo/article/details/8159176

继续阅读