Linux下一套通用的线程库,遵循POSIX线程接口,称为pthread,广泛的被各种Unix所支持, 是由POSIX提出的. 因此, 它具有很好的移植性. 由于它是通过内核级线程来实现的, 就没有完全的实现它. 但从功能上来看, 它丝毫不逊色.
编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。编译的时候需要-lpthread。
一.线程相关函数
1.int pthread_create (pthread_t *THREAD, pthread_attr_t * ATTR, void * (*START_ROUTINE(void *), void * ARG); 2.int pthread_join(pthread_t TH, void **thread_RETURN); |
二.Mutex相关
Mutex用于解决互斥问题. 一个Mutex是一个互斥装置, 用于保护临界区和共享内存. 它有两种状态locked, unlocked. 它不能同时被两个线程所拥有.
下面的函数用于处理Mutex:
int pthread_mutex_init (pthread_mutex_t *MUTEX, const pthread_mutexattr_t *MUTEXATTR);//初始化 int pthread_mutex_lock (pthread_mutex_t *mutex));//锁定一个Mutex int pthread_mutex_trylock (pthread_mutex_t *MUTEX);//试图锁定一个Mutex int pthread_mutex_unlock (pthread_mutex_t *MUTEX);//结锁一个Mutex int pthread_mutex_destroy (pthread_mutex_t *MUTEX);//销毁一个Mutext |
通常, 我们用一些静态变量来初始化mutex.
PTHREAD_MUTEX_INITIALIZER PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP |
注意: _NP 表示no portable不可移植
例如:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
... ...
pthread_mutext_lock(&mutex);
fwrite(buffer, 1, strlen(buffer), file);
pthread_mutex_unlock(&mutex);
... ...
附上实例:
// pthread_create.cpp
/*
* pthread_create, pthread_mutex, pthread_join
* */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
// thread id.
pthread_t ntid;
// mutex object.
pthread_mutex_t mutex;
int count;
void printids(const char* s)
{
pid_t pid = getpid();
pthread_t tid = pthread_self();
printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int)pid,
(unsigned int)tid, (unsigned int)tid);
}
void* thread_func(void* arg)
{
printids("new thread beign\n");
// lock mutex
pthread_mutex_lock(&mutex);
printids("new thread:");
for(int i = 0; i < 5; ++i)
{
printf("thread_func running %d\n", count++);
}
// unlock mutex
pthread_mutex_unlock(&mutex);
int* ptr = new int(100);
return (void*)ptr;
}
int main(int argc, char* argv[])
{
count = 0;
// initialize mutex object
pthread_mutex_init(&mutex, NULL);
// create thread
int err = pthread_create(&ntid, NULL, thread_func, NULL);
if(0 != err)
{
printf("can`t create thread:%s\n", strerror(err));
}
pthread_mutex_lock(&mutex);
printids("main thread:");
for(int i = 0; i < 5; ++i)
{
printf("main running %d \n", count++);
}
sleep(1);
pthread_mutex_unlock(&mutex);
void* ret = NULL;
pthread_join(ntid, &ret);
printf("thread_func return %d\n", *((int*)ret));
pthread_mutex_destroy(&mutex);
return 0;
}
// Makefile
# this is a Makefile
#
.PHONY: all
all: test
test:
g++ -g thread_create.cpp -lpthread -o test
clean:
rm -f test
三.Condition Variable(条件变量)
也是一种用于同步的device. 允许一个进程将自己挂起等待一个条件变量被改变状态.
有下列几个函数:
int pthread_cond_init (pthread_cond_t *COND, pthread_condattr_t *cond_ATTR);
const struct timespec *ABSTIME);
int pthread_cond_destroy (pthread_cond_t *COND); |
看名字就可以知道它们的用途了. 通常我们也使用静态变量来初始化一个条件变量.
例如:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_cond_signal 用于唤醒一个被锁定的线程. pthread_cond_broadcast 用于唤醒所有被锁定的线程. pthread_cont_wait 用于等待. |
为了解决竞争问题(即一个线程刚要去wait而另一个线程已经signal了), 它要与一个mutex连用.
看一看下面的例子:
int x,y;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//Waiting until X is greater than Y is performed as follows:
pthread_mutex_lock(&mut);
while (x <= y) {
pthread_cond_wait(&cond, &mut);
}
pthread_mutex_unlock(&mut);
pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。
但使用pthread_cond_signal不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一个pthread_cond_signal调用最多发信一次。
另外,互斥量的作用一般是用于对某个资源进行互斥性的存取,很多时候是用来保证操作是一个原子性的操作,是不可中断的。
pthread_cond_wait的执行过程如下:
1. 首先, 它unlock the mutex, then 挂起当前的线程.
2. 当被唤醒的时候, 它会lock the mutex.
这样就保证了这是一个临界区.
附上实例:
//conv.cpp
/*
* this is an example using condition variable.
* */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void *thread1(void *);
void *thread2(void *);
int i=1;
int main(void)
{
pthread_t t_a;
pthread_t t_b;
pthread_create(&t_a,NULL,thread1,(void *)NULL);
pthread_create(&t_b,NULL,thread2,(void *)NULL);
pthread_join(t_a, NULL);
pthread_join(t_b, NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}
void *thread1(void *junk)
{
for(i = 1; i <= 6; ++i)
{
pthread_mutex_lock(&mutex);
printf("thread1: lock %d\n", __LINE__);
if(i%3 == 0)
{
printf("thread1:signal 1 %d\n", __LINE__);
pthread_cond_signal(&cond);
printf("thread1:signal 2 %d\n", __LINE__);
sleep(1);
}
pthread_mutex_unlock(&mutex);
printf("thread1: unlock %d\n\n", __LINE__);
sleep(1);
}
}
void *thread2(void *junk)
{
while(i < 6)
{
pthread_mutex_lock(&mutex);
printf("thread2: lock %d\n", __LINE__);
if(i%3 != 0)
{
printf("thread2: wait 1 %d\n", __LINE__);
pthread_cond_wait(&cond, &mutex);
printf("thread2: wait 2 %d\n", __LINE__);
}
pthread_mutex_unlock(&mutex);
printf("thread2: unlock %d\n\n", __LINE__);
sleep(1);
}
}
运行结果如下:
thread1: lock 35
thread1: unlock 44
thread2: lock 54
thread2: wait 1 57
thread1: lock 35
thread1: unlock 44
thread1: lock 35
thread1:signal 1 38
thread1:signal 2 40
thread1: unlock 44
thread2: wait 2 59
thread2: unlock 62
thread2: lock 54
thread2: unlock 62
thread1: lock 35
thread1: unlock 44
thread2: lock 54
thread2: wait 1 57
thread1: lock 35
thread1: unlock 44
thread1: lock 35
thread1:signal 1 38
thread1:signal 2 40
thread1: unlock 44
thread2: wait 2 59
thread2: unlock 62
说明:
这里关键是pthread_cond_signal和pthread_cond_wait两个函数,当线程2执行pthread_cond_wait之后,线程2阻塞,在等待条件变量cond,同时mutex处于解锁状态,当线程1执行pthread_cond_signal触发信号后,条件变量cond改变,但由于mutex还未解锁,所以线程2仍然处于阻塞状态,直到线程1解锁才会轮到线程2执行
,线程2触发之后会先加锁mutex,执行完之后再对mutex解锁。
四. Thread-Specific Data (TSD)
五. 信号处理
六. 放弃 (Cancellation)
七. 清理函数 (Cleanup Handlers)
八. 信号量 (Semaphores)