#include
int pthread_equal(pthread_t tid1, pthread_t tid2);
//返回值:若相等,返回非0数值;否则,返回0
#include
pthread_t pthread_slef(void);
//返回值:调用线程的线程ID
线程创建的原型为:
#include
int pthread_create(pthread_t *restrict tidp,
const pthread_attr_t *reatrict attr,
void *(*start_rtn)(void *),
void *restrict arg);
//返回值:若成功则返回0;否则,返回错误编号
start_rtn
函数开始运行,该函数参数为void*
类型,返回值也为void*
例子:创建一个新线程并打印线程id
#include
#include
#include
#include
pthread_t ntid;
void printids(const char* s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();//get thread id
printf("%s pid %lu tid %lu (0x%lx)\n", s, (unsigned long)pid,
(unsigned long)tid, (unsigned long)tid);
}
void* thr_fn(void* args)
{
printids("new thread:");
return (void*)0;
}
int main()
{
int err;
err = pthread_create(&ntid, NULL, thr_fn, NULL);
if (err != 0)
perror("can't create thread!");
printids("main thread:");
sleep(1);
exit(0);
}
main thread: pid 9013 tid 139851889776448 (0x7f31ce361740)
new thread: pid 9013 tid 139851881416448 (0x7f31cdb68700)
进程中调用exit
、_Exit
、_exit
,那么整个进程会终止。
单个线程3种方式退出:
thread_exit
#include
void pthread_exit(void *rval_ptr);
* `rval_ptr`是一个无类型指针,与传给启动例程的单个参数类似。
* 进程中其他线程可以访问该指针
```
#include
int pthread_jion(pthread_t thread, void **rval_ptr);
//返回值:若成功,返回0;否则,返回错误编号
```
* 调用线程将一直阻塞,直到指定的线程调用pthread_exit
、从启动例程中返回或者被取消。如果线程简单的从它的启动例程返回,rval_ptr
就包含返回码。如果线程被取消,由rval_ptr
指定的内存单元就设设置为PTHREAD_CANCELED
。如果对线程返回值不感兴趣,那么可以把rval_ptr
设置为NULL
。
例子:获得线程的退出状态
#include
#include
#include
#include
void* thr_fn1(void* arg)
{
printf("thread 1 returning\n");
return (void*)1;
}
void* thr_fn2(void* arg)
{
printf("thread 2 returning\n");
pthread_exit((void*)2);
}
int main()
{
int err;
pthread_t tid1, tid2;
void *tret;
err = pthread_create(&tid1, NULL, thr_fn1, NULL);
if (err != 0)
{
perror("can't create thread 1.\n");
exit(1);
}
err = pthread_create(&tid2, NULL, thr_fn2, NULL);
if (err != 0)
{
perror("can't create thread 2.\n");
exit(1);
}
err = pthread_join(tid1, &tret); // get thread1 return code
if (err != 0)
{
perror("can't join with thread1.\n");
exit(1);
}
printf("thread1 exit code : %ld\n", (long)tret);
err = pthread_join(tid2, &tret);
if (err != 0)
{
perror("can't join thread 2.\n");
exit(1);
}
printf("thread 2 exit code is %ld\n", (long)tret);
exit(0);
}
输出:
thread 2 returning
thread 1 returning
thread1 exit code : 1
thread 2 exit code is 2
线程可以通过pthread_cancel
函数来请求取消同一进程中的其他线程。
#include
int pthread_cancel(pthread_t tid)
//返回值:成功返回0,错误返回错误码
PTHREAD_CANCELED
的pthread_exit
函数。线程可以安排它退出的函数,这与进程在退出时可以用atexit
函数一样。这样的函数为线程清理函数。注意:一个线程可以有多个线程清理函数,处理程序记录在栈上,这与它们注册时相反。
#include
void pthread_cleanup_push(void (*rtn)(void*), void* arg);
void pthread_cleanup_pop(int execute);
当线程执行以下动作时,清理函数rtn是由pthread_cleanup_push函数调度的,调用是只有一个参数arg:
如果execute参数设置为0,清理函数将不被调用。不管发生哪种情况。pthread_cleanup_pop都将删除上次pthread_cleanup_push调用建立的情路处理程序。
一个程序清理处理程序:
#include
#include
#include
#include
void cleanup(void *arg)
{
printf("clearnup: %s", (char*) arg);
}
void* thr_fn1(void* arg)
{
printf("thread 1 start.\n");
pthread_cleanup_push(cleanup, "thread 1 first handle");
pthread_cleanup_push(cleanup, "thread 1 second handle");
printf("thread1 push complete\n");
if (arg)
return (void*)1;
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
return (void*)1;
}
void* thr_fn2(void* arg)
{
printf("thread 2 start.\n");
pthread_cleanup_push(cleanup, "thread 2 first handle");
pthread_cleanup_push(cleanup, "thread 2 second handle");
if (arg)
return (void*)1;
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
return (void*)1;
}
int main()
{
int err;
pthread_t tid1, tid2;
void *tret;
err = pthread_create(&tid1, NULL, thr_fn1, (void*)1);
if (err != 0)
{
printf("create thread1 error!\n");
exit(1);
}
err = pthread_create(&tid2, NULL, thr_fn2, (void*)1);
if (err != 0)
{
perror("create thread2 error!\n");
exit(1);
}
err = pthread_join(tid1, &tret);
if (err != 0)
{
perror("join thread 1 error!\n");
exit(1);
}
printf("thread 1 exit code : %ld\n",(long)tret );
err = pthread_join(tid2, &tret);
if (err != 0)
{
perror("join thread 2 error!\n");
exit(1);
}
printf("thread 2 exit code : %ld\n", (long)tret);
exit(0);
}
默认情况下,线程的终止状态会保持到对该线程调用pthread_join
。如果线程已经分离,则线程底层存储资源可以在线程终止时立即收回。可以用函数pthread_detach
分离
#include
int pthread_detach(pthread_t tid);
例子:如果有两个线程A和B,对全局变量a=0执行相同的操作a++
,那么a最后的结果会是多少呢?如果是顺序执行,A和B不存在竞争关系,那么就是2,但是由于a++
不是原子操作。需要3步完成。1.从内存读入寄存器;2.寄存器中对变量增加;3.把新值写会内存。这样可能为1,也可能为2.
为解决上述问题,我们需要使用pthread的互斥接口保护数据,保证在同一时间只有一个线程访问数据。
互斥锁的使用过程为:
加锁后其他线程会被阻塞直到当前线程释放该互斥锁。
那么互斥锁是怎么样的呢?
结构体pthread_mutex_t
表示互斥变量。使用互斥变量之前,必须首先对它进行初始化,可以把它设置为常量PTHREAD_MUTEX_INITIALIZER
(静态),也可以通过调用pthread_mutex_init
函数进行初始化。如果动态分配互斥量,在释放内存前需要调用pthread_mutex_destroy
。
#include
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//两个函数的返回值:若成功,返回0;否则,返回错误编号
attr
设为NULL
。互斥量加锁和解锁的函数
#include
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//所有函数的返回值:若成功,返回0;否则,返回错误编号
pthread_mutex_lock
: 如果互斥量已经被其他线程加锁,那么会阻塞,直到该互斥量被解锁再对互斥量加锁。pthread_mutex_trylock
:如果互斥量已经被其他线程加锁,则函数返回错误。如果未被加锁,那么就对互斥量加锁。pthread_mutex_unlock
:解锁下面是一个用mutex来保护数据结构的例子:
#include
#include
#include
#include
#include
struct foo
{
int f_count;
pthread_mutex_t f_lock;
int f_id;
};
struct foo* foo_alloc(int id)
{
struct foo *fp;
if ((fp = (struct foo*)(malloc(sizeof(struct foo)))) != NULL)
{
fp->f_count = 1;
fp->f_id = id;
if (pthread_mutex_init(&fp->f_lock, NULL) != 0)
{
free(fp);
return NULL;
}
}
return fp;
}
void foo_rela(struct foo* fp)
{
pthread_mutex_lock(&fp->f_lock);
if (--fp->f_count == 0)
{
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_destroy(&fp->f_lock);
free(fp);
}
else
{
pthread_mutex_unlock(&fp->f_lock);
}
}
pthread_mutex_lock(mutex);
pthread_mutex_lock(mutex);
pthread_mutex_unlock(mutex);
pthread_mutex_unlock(mutex);
可以先释放占有的锁,过一段时间再试
pthread_mutex_timedlock
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,const struct timespec *restrict tsptr);
pthread_mutex_timedlock
:尝试加锁,阻塞(最多阻塞到时间点tsptr),如果在tsptr还无法加锁,则返回错误。下面是一个例子:
#include
#include
#include
#include
#include
#include
int main()
{
int err;
struct timespec tout;
struct tm *tmp;
char buf[64];
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&lock);
printf("mutex is locked!\n");
clock_gettime(CLOCK_REALTIME, &tout);
tmp = localtime(&tout.tv_sec);
strftime(buf, sizeof(buf), "%r", tmp);
printf("current time is %s\n", buf);
tout.tv_sec +=10;
err = pthread_mutex_timedlock(&lock, &tout);
clock_gettime(CLOCK_REALTIME, &tout);
tmp = localtime(&tout.tv_sec);
strftime(buf, sizeof(buf), "%r", tmp);
printf("the time is now %s\n", buf);
if (err == 0)
{
printf("mutex locked again!\n");
}
else
{
printf("Can't lock mutex again:%s\n", strerror(err));
}
exit(0);
}
mutex is locked!
current time is 08:45:01 PM
the time is now 08:45:11 PM
Can't lock mutex again:Connection timed out
读写锁对于为资源共享提高了并行性。
读写锁的策略:
读写锁必须初始化
初始化和销毁函数:
#include
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destory(pthread_rwlock_t *rwlock);
//两个函数的返回值:若成功,返回0;否则,返回错误编号
加锁和解锁函数
#include
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
//所有函数的返回值:若成功,返回0;否则,返回错误编号
pthread_rwlock_rdlock
读加锁pthread_rwlock_wrlock
写加锁pthread_rwlock_unlock
解锁读写锁原语的条件版本
#include
int pthread_rwlock_trylock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trylcok(pthread_rwlock_t *rwlock);
//两个函数的返回值:若成功,返回0;否则,返回错误编码
#include
#include
int pthread_rwlock_timedrdlock(
pthread_rwlock_t *restrict rwlock,
const struct timespec *restrict tsptr);
int pthread_rwlock_timedwrlock(
pthread_rwlock_t *restrict rwlock,
const struct timespec *restrict tsptr);
//两个函数的返回值:若成功,返回0;否则,返回错误编号
参考:
http://www.cnblogs.com/newlist/archive/2012/02/11/2346284.html
http://www.wuzesheng.com/?p=1668
条件变量(Condtion Variable)是在多线程程序中用来实现“等待->唤醒”逻辑常用的方法。举个简单的例子,应用程序A中包含两个线程t1和t2。t1需要在bool变量test_cond为true时才能继续执行,而test_cond的值是由t2来改变的,这种情况下,如何来写程序呢?可供选择的方案有两种:
第一种是t1定时的去轮询变量test_cond,如果test_cond为false,则继续休眠;如果test_cond为true,则开始执行。
第二种就是上面提到的条件变量,t1在test_cond为false时调用cond_wait进行等待,t2在改变test_cond的值后,调用cond_signal,唤醒在等待中的t1,告诉t1 test_cond的值变了,这样t1便可继续往下执行。
很明显,上面两种方案中,第二种方案是比较优的。在第一种方案中,在每次轮询时,如果t1休眠的时间比较短,会导致cpu浪费很厉害;如果t1休眠的时间比较长,又会导致应用逻辑处理不够及时,致使应用程序性能下降。第二种方案就是为了解决轮询的弊端而生的。
与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。
条件变量可以使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立”(给出条件成立信号)。条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。
条件变量分为两部分: 条件和变量. 条件本身是由互斥量保护的. 线程在改变条件状态前先要锁住互斥量. 它利用线程间共享的全局变量进行同步的一种机制。
条件变量的结构体为pthread_cond_t
条件变量的初始化:
#include
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
//返回值:成功返回0,否则返回错误码
PTHREAD_COND_INITIALIZER
静态初始化。使用pthread_cond_wait
等待条件变量变为真。如果在给定的时间内条件不能满足,则会生成错误码。
#include
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,
const struct timespec *restrict tsptr);
pthread_cond_wait
函数会发生下面过程: 唤醒函数:
#include
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
使用:
(1)初始化.init()
或者pthread_cond_t cond=PTHREAD_COND_INITIALIER
;属性置为NULL
(2)等待条件成立.pthread_wait
,pthread_timewait.wait()
释放锁,并阻塞,等待条件变量为真
(3)激活条件变量:pthread_cond_signal
,pthread_cond_broadcast
(激活所有等待线程)
(4)清除条件变量:destroy
;无线程等待,否则返回EBUSY
主线程:
线程A:
线程B:
一个例子:
// 使用条件变量的一个例子:
//
#include
#include
#include
#include
#define NUM_THREAD 3//线程数目
#define TCOUNT 5 //单线程轮询次数
#define COUNT_LIMIT 7//发送信号的次数
int count = 0; //全局累加量
pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;
//增加count
void *inc_count(void *t)
{
int i;
long my_id = (long)t;
for (i = 0; i < TCOUNT; ++i)
{
pthread_mutex_lock(&count_mutex);
count++;
//检查count的值,满足条件就发送信号
if (count < COUNT_LIMIT)
{
printf("inc_count(): thread %ld, count = %d Threshold reached. ",
my_id, count);
//发送信号
pthread_cond_signal(&count_threshold_cv);
printf("Just sent signal.\n");
}
printf("inc_count() : thread %ld, count = %d, unlocking muext.\n",
my_id, count);
pthread_mutex_unlock(&count_mutex);
sleep(1);
}
pthread_exit(NULL);
}
//等待线程
void *watch_count(void* t)
{
long my_id = (long)t;
printf("Starting watch_count(): thread %ld.\n", my_id);
//锁定互斥量并等待信号,以原子的方式解锁互斥量。
while ( count < COUNT_LIMIT)
{
pthread_mutex_lock(&count_mutex);
printf("watch_count(): thread %ld go inot wait...\n", my_id);
pthread_cond_wait(&count_threshold_cv, &count_mutex);
printf("watch_count():thread %ld Condition signal received.\n", my_id);
printf("watch_count():thread %ld count now = %d.\n", my_id, count);
pthread_mutex_unlock(&count_mutex);
}
pthread_exit(NULL);
}
int main()
{
int i;
long t1 =1, t2 = 2, t3 = 3;
pthread_t threads[3];
pthread_attr_t attr;
//初始化互斥量和条件变量对象
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[0], &attr, watch_count, (void*) t1);
pthread_create(&threads[1], &attr, inc_count, (void*) t2);
pthread_create(&threads[2], &attr, inc_count, (void*) t3);
for (int i= 1; i < NUM_THREAD; ++i)
{
pthread_join(threads[i], NULL);
}
//发送信号个i额监听线程
//
pthread_cond_signal(&count_threshold_cv);
pthread_join(threads[0], NULL);
printf("Main():Waited on %d theaads, final value of ocunt = %d.Done\n",
NUM_THREAD, count);
//清理线程
//
pthread_attr_destroy(&attr);
pthread_mutex_destroy(&count_mutex);
pthread_cond_destroy(&count_threshold_cv);
pthread_exit(NULL);
}
inc_count(): thread 3, count = 1 Threshold reached. Just sent signal.
inc_count() : thread 3, count = 1, unlocking muext.
inc_count(): thread 2, count = 2 Threshold reached. Just sent signal.
inc_count() : thread 2, count = 2, unlocking muext.
Starting watch_count(): thread 1.
watch_count(): thread 1 go inot wait...
inc_count(): thread 3, count = 3 Threshold reached. Just sent signal.
inc_count() : thread 3, count = 3, unlocking muext.
watch_count():thread 1 Condition signal received.
watch_count():thread 1 count now = 3.
watch_count(): thread 1 go inot wait...
inc_count(): thread 2, count = 4 Threshold reached. Just sent signal.
inc_count() : thread 2, count = 4, unlocking muext.
watch_count():thread 1 Condition signal received.
watch_count():thread 1 count now = 4.
watch_count(): thread 1 go inot wait...
inc_count(): thread 3, count = 5 Threshold reached. Just sent signal.
inc_count() : thread 3, count = 5, unlocking muext.
inc_count(): thread 2, count = 6 Threshold reached. Just sent signal.
inc_count() : thread 2, count = 6, unlocking muext.
watch_count():thread 1 Condition signal received.
watch_count():thread 1 count now = 6.
watch_count(): thread 1 go inot wait...
inc_count() : thread 2, count = 7, unlocking muext.
inc_count() : thread 3, count = 8, unlocking muext.
inc_count() : thread 3, count = 9, unlocking muext.
inc_count() : thread 2, count = 10, unlocking muext.
watch_count():thread 1 Condition signal received.
watch_count():thread 1 count now = 10.
Main():Waited on 3 theaads, final value of ocunt = 10.Done
自旋锁在获取锁之前一直处于忙等待状态。自旋锁的作用是提高加锁的效率。它一般用于使用者保持锁时间较短的情况。
自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用。如果被保护的共享资源只在进程上下文访问,使用信号量保护该共享资源非常合适,如果对共享资源的访问时间非常短,自旋锁也可以。但是如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。自旋锁保持期间是抢占失效的,而信号量和读写信号量保持期间是可以被抢占的。自旋锁只有在内核可抢占或SMP(多处理器)的情况下才真正需要,在单CPU且不可抢占的内核下,自旋锁的所有操作都是空操作。另外格外注意一点:自旋锁不能递归使用。
自旋锁的一些函数:
初始化和销毁:
#include
int pthread_spin_init(pthread_spinlock_t *lock,intpshared);
int pthread_spin_destroy(pthread_spinlock_t *lock);
//成功返回0,失败返回错误编码
加锁和解锁:
#include
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);
//成功返回0,失败返回错误码
屏障允许多个线程等待,直到所有合作的线程到达某个点。
初始化和销毁函数:
#include
int pthread_barrier_init(pthread_barrier_t *restrictbarrier,const pthread_barrierattr_t *restrict attr,unsigned int count);
int pthread_barrier_destroy(pthread_barrier_t *barrier);
//成功返回0,失败返回错误码
告知线程已完成工作,准备所有其他线程赶过来
#include
int pthread_barrier_wait(pthread_barrier_t *barrier);
//成功返回0,失败返回错误码
使用内存屏障的一个例子:
#include
#include
#include
#include
#include
#include
#include
#include
#define NTHR 8
#define NUMNUM 8000000L
#define TNUM (NUMNUM/NTHR)
long nums[NUMNUM];
long snums[NUMNUM];
pthread_barrier_t b;
extern void qsort(void*, size_t, size_t, int(*)(const void*, const void*));
int complong(const void* arg1, const void* arg2)
{
long l1 = *(long *)arg1;
long l2 = *(long *)arg2;
if (l1 == l2)
return 0;
if (l1 < l2)
return -1;
return 1;
}
void *thr_fn(void *arg)
{
long idx = (long)arg;
qsort(&nums[idx], TNUM, sizeof(long), complong);
pthread_barrier_wait(&b);
return (void*)0;
}
void merge()
{
long idx[NTHR];
long i, minidx, sidx, num;
for(i = 0; i < NTHR; ++i)
{
idx[i] = i * TNUM;
}
for (sidx = 0; sidx < NUMNUM; ++sidx)
{
num = LONG_MAX;
for (i = 0; i < NTHR; ++i)
{
if ((idx[i] < (i+1)*TNUM) && (nums[idx[i]] < num))
{
num = nums[idx[i]];
minidx = i;
}
}
snums[sidx] = nums[idx[minidx]];
idx[minidx]++;
}
}
int main()
{
unsigned long i;
struct timeval start, end;
long long startusec, endusec;
double elapsed;
int err;
pthread_t tid;
srandom(1);
for (int i = 0; i < NUMNUM; ++i)
nums[i] = random();
gettimeofday(&start, NULL);
pthread_barrier_init(&b, NULL, NTHR+1);
for (int i = 0; i < NTHR; ++i)
{
err = pthread_create(&tid, NULL, thr_fn, (void*)(i * TNUM));
if (err != 0)
{
perror("Can't create thread!\n");
exit(1);
}
}
pthread_barrier_wait(&b);
merge();
gettimeofday(&end, NULL);
startusec = start.tv_sec * 1000000 + start.tv_usec;
endusec = end.tv_sec * 1000000 + end.tv_usec;
elapsed = (double)(endusec - startusec)/1000000;
printf("sort took %.4f seconds\n", elapsed);
// for(i = 0; i< NUMNUM; ++i)
// printf("%ld\n", snums[i]);
exit(0);
}