http://21cnbao.blog.51cto.com/109393/120039
1.Linux“线程”
笔者曾经在《基于嵌入式操作系统VxWorks的多任务并发程序设计》(《软件报》2006年第5~12期)中详细叙述了进程和线程的区别,并曾经说明Linux是一种“多进程单线程”的操作系统。Linux本身只有进程的概念,而其所谓的“线程”本质上在内核里仍然是进程。大家知道,进程是资源分配的单位,同一进程中的多个线程共享该进程的资源(如作为共享内存的全局变量)。Linux中所谓的“线程”只是在被创建的时候“克隆”(clone)了父进程的资源,因此,clone出来的进程表现为“线程”,这一点一定要弄清楚。因此,Linux“线程”这个概念只有在打冒号的情况下才是最准确的,可惜的是几乎没有书籍留心去强调这一点。
Linux内核只提供了轻量进程的支持,未实现线程模型,但Linux尽最大努力优化了进程的调度开销,这在一定程度上弥补无线程的缺陷。Linux用一个核心进程(轻量进程)对应一个线程,将线程调度等同于进程调度,交给核心完成。
目前Linux中最流行的线程机制为LinuxThreads,所采用的就是线程-进程“一对一”模型,调度交给核心,而在用户级实现一个包括信号处理在内的线程管理机制。LinuxThreads由Xavier Leroy (
Xavier.Leroy@inria.fr)负责开发完成,并已绑定在GLIBC中发行,它实现了一种BiCapitalized面向Linux的Posix 1003.1c “pthread”标准接口。Linuxthread可以支持Intel、Alpha、MIPS等平台上的多处理器系统。
按照POSIX 1003.1c 标准编写的程序与Linuxthread 库相链接即可支持Linux平台上的多线程,在程序中需包含头文件pthread. h,在编译链接时使用命令:
pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
pthread_self (void) ;
线程退出
线程的退出方式有三:
(1)执行完成后隐式退出;
(2)由线程本身显示调用pthread_exit 函数退出;
pthread_cance (pthread_t thread) ;
在某线程中调用此函数,可以终止由参数thread 指定的线程。
如果一个线程要等待另一个线程的终止,可以使用pthread_join函数,该函数的作用是调用pthread_join的线程将被挂起直到线程ID为参数thread的线程终止:
int x; //
pthread_mutex_t mutex;
按缺省的属性初始化互斥体变量
pthread_mutex_lock(&mutex); //
… //
x
phtread_mutex_unlock(&mutex); //
pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *attr);
pthread_cond_wait和pthread_cond_timedwait用来等待条件变量被设置,值得注意的是这两个等待调用需要一个已经上锁的互斥体mutex,这是为了防止在真正进入等待状态之前别的线程有可能设置该条件变量而产生竞争。pthread_cond_wait的函数原型为:
pthread_cond_broadcast (pthread_cond_t *cond) ;
pthread_cond_signal则用于解除某一个等待线程的阻塞状态:
sem_init(sem_t *sem, int pshared, unsigned int val);
这个函数初始化一个信号量sem 的值为val,参数pshared 是共享属性控制,表明是否在进程间共享。
sem_post(sem_t *sem);
调用该函数,信号量sem的值增加,可以从无信号状态变为有信号状态。
4.实例
下面我们还是以著名的生产者/消费者问题为例来阐述Linux线程的控制和通信。一组生产者线程与一组消费者线程通过缓冲区发生联系。生产者线程将生产的产品送入缓冲区,消费者线程则从中取出产品。缓冲区有N 个,是一个环形的缓冲池。
#include <pthread.h>
缓冲区数量
{
缓冲区相关数据结构
实际数据存放的数组
pthread_mutex_t lock; /*
lock
*/
读写指针
pthread_cond_t notempty; /*
*/
缓冲区未满的条件变量
};
初始化缓冲区结构
void init(struct prodcons *b)
pthread_mutex_init(&b->lock, NULL);
pthread_cond_init(&b->notfull, NULL);
b->writepos = 0;
/*
,
*/
{
/*
*/
{
}
写数据
并移动指针
b->buffer[b->writepos] = data;
if (b->writepos > = BUFFER_SIZE)
/*
*/
pthread_mutex_unlock(&b->lock);
从缓冲区中取出整数
int get(struct prodcons *b)
int data;
/*
*/
{
}
读数据
移动读指针
data = b->buffer[b->readpos];
if (b->readpos > = BUFFER_SIZE)
/*
*/
pthread_mutex_unlock(&b->lock);
}
/*
:
1
10000
,
程从缓冲区中获取整数
两者都打印信息
#define OVER ( - 1)
void *producer(void *data)
int n;
{
put(&buffer, n);
return NULL;
{
while (1)
d = get(&buffer);
break;
}
}
int main(void)
pthread_t th_a, th_b;
init(&buffer);
创建生产者和消费者线程
pthread_create(&th_a, NULL, producer, 0);
/*
*/
pthread_join(th_b, &retval);
}
5.WIN32、VxWorks、Linux线程类比
目前为止,笔者已经创作了《基于嵌入式操作系统VxWorks的多任务并发程序设计》(《软件报》2006年5~12期连载)、《深入浅出Win32多线程程序设计》(天极网技术专题)系列,我们来找出这两个系列文章与本文的共通点。
看待技术问题要瞄准其本质,不管是Linux、VxWorks还是WIN32,其涉及到多线程的部分都是那些内容,无非就是线程控制和线程通信,它们的许多函数只是名称不同,其实质含义是等价的,下面我们来列个三大操作系统共同点详细表单:
事项
|
WIN32
|
VxWorks
|
Linux
|
线程创建
|
CreateThread
|
taskSpawn
|
pthread_create
|
线程终止
|
执行完成后退出;线程自身调用ExitThread 函数即终止自己;被其他线程调用函数TerminateThread函数
|
执行完成后退出;由线程本身调用exit退出;被其他线程调用函数taskDelete终止
|
执行完成后退出;由线程本身调用pthread_exit 退出;被其他线程调用函数pthread_cance终止
|
获取线程ID
|
GetCurrentThreadId
|
taskIdSelf
|
pthread_self
|
创建互斥
|
CreateMutex
|
semMCreate
|
pthread_mutex_init
|
获取互斥
|
WaitForSingleObject、
WaitForMultipleObjects
|
semTake
|
pthread_mutex_lock
|
释放互斥
|
ReleaseMutex
|
semGive
|
phtread_mutex_unlock
|
创建信号量
|
CreateSemaphore
|
semBCreate、semCCreate
|
sem_init
|
等待信号量
|
WaitForSingleObject
|
semTake
|
sem_wait
|
释放信号量
|
ReleaseSemaphore
|
semGive
|
sem_post
|
6.小结
本章讲述了Linux下多线程的控制及线程间通信编程方法,给出了一个生产者/消费者的实例,并将Linux的多线程与WIN32、VxWorks多线程进行了类比,总结了一般规律。鉴于多线程编程已成为开发并发应用程序的主流方法,学好本章的意义也便不言自明。