> 文章列表 > 【线程编程】线程编程之Pthreads

【线程编程】线程编程之Pthreads

【线程编程】线程编程之Pthreads

C语言提供了多种多线程并发的框架和库,其中最常用的是 POSIX线程库(Pthreads)。
Pthreads库提供了一套标准的API,使得开发者可以轻松地编写多线程并发的程序。这是一套由POSIX提出的通用的线程库,在Linux平台下被广泛支持。使用pthread库需要包含头文件<pthread.h>,并在编译时加上-lpthread选项。使用pthread库可以创建、终止、等待、同步和管理线程。

线程的创建与终止

一个简单的例子:

#include <pthread.h>
#include <stdio.h>
#define THRDS 5void *PrintHello(void *t) {printf("Hello World! It's me, thread #%ld!\\n", (long)t);pthread_exit(NULL);
}int main () {pthread_t callThd[THRDS];for(long t=0; t<THRDS; t++){int rc = pthread_create(&callThd[t], NULL, PrintHello, (void *)t);if (rc){printf("ERROR: pthread_create() return %d\\n", rc);return -1;}}pthread_exit(NULL);}

上面我们用到了 pthread_create 来创建线程。

int pthread_create(pthread_t *thread, // 线程 IDconst pthread_attr_t *attr, // 线程属性,NULL 则采用默认属性void *(* start_routine)(void *), // 要线程化的函数的指针void *arg); // 传递给 start_routine 函数的参数

线程函数的参数必须通过引用传递并转换为(void *)。
若要传递多个参数,可创建一个包含所有参数的结构体,再传递指向该结构体的指针。
如果传递的参数是一个变量的地址,由于这是共享内存空间,变量对所有线程可见,很有可能在新线程访问它之前,此内存位置的值发生了更改。

终止一个线程有下面几种方法

  1. 线程正常执行完后返回。
  2. 线程调用 pthread_exit。
  3. 线程被另一个线程通过 pthread_cancel 取消。
  4. 整个进程因调用 exec() 或 exit() 而终止。
  5. main() 先完成,且没有显式调用 pthread_exit 。
    如果没有显式地调用 pthread_exit(), main() 就会在它产生的线程之前完成,那么所有线程都将终止。
    显示调用 pthread_exit(),则main() 会在结束前等待所有线程执行完毕。
    我们也可以在 main() 中调用 pthread_join(t, NULL); 来连接子线程,连接后,当前线程就会阻塞并等待子线程 t 的结束。
    另外创建时线程时可以通过线程属性指定是否可被连接。

线程协调和同步

Unix 的常见的线程同步机制:互斥(mutex)、信号量(semaphore)和条件变量(condition variable)。
pthread 库提供的三种同步机制:

  1. 互斥锁:阻止其他线程访问变量。
  2. 连接(join):让一个线程等待,直到其他人终止。(上面已经提到)
  3. 条件变量:数据类型 pthread_cond_t。

互斥
Mutex是“互斥”(mutual exclusion)的缩写。
一个简单的例子:

#include <pthread.h>
#include <stdio.h>
#define THRDS 5
pthread_t callThd[THRDS];
pthread_mutex_t mutexsum;
long sum;void *add(void*) {pthread_mutex_lock(&mutexsum);sum++;pthread_mutex_unlock(&mutexsum);return 0;
}
int main(int argc, char *argv[]) {pthread_mutex_init(&mutexsum, NULL);for(int i = 0; i < THRDS; i++) {pthread_create(&callThd[i], NULL, add, NULL);}for(int i = 0; i < THRDS; i++) {pthread_join(callThd[i], NULL);}printf("Sum =  %ld \\n", sum);pthread_mutex_destroy(&mutexsum);return 0;
}

互斥变量必须声明为pthread_mutex_t类型,并且必须在可以使用它们之前进行初始化。有两种方法来初始化互斥变量:

  1. pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
  2. 使用pthread_mutex_init()。该方法允许设置互斥对象属性 attr。

互斥变量最初是未上锁的。

条件变量
一个条件变量总是与一个互斥锁一起使用。

include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define THRDS  3
#define TCOUNT 10
#define COUNT_LIMIT 12int count = 0;
pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;void *inc_count(void *t) {for (int i = 0; i < TCOUNT; i++) {pthread_mutex_lock(&count_mutex);/* 检查是否数量达到阈值 */if (++count == COUNT_LIMIT) {printf("inc_count(): 线程 %ld, count = %d  达到阈值. ", (long)t, count);/* pthread_cond_signal 用来唤醒正在等待对应条件变量的线程,此时互斥锁必须是锁住的。执行之后必须用 pthread_mutex_unlock 解锁互斥锁。若有多个线程在等待条件变量,那么必须用 pthread_cond_broadcast 代替 pthread_cond_signal。必须在调用 pthread_cond_signal 之前调用 pthread_cond_wait。*/pthread_cond_signal(&count_threshold_cv);}printf("inc_count(): 线程 %ld, count = %d \\n", (long)t, count);pthread_mutex_unlock(&count_mutex);/* 稍微等待一会儿 */sleep(1);}pthread_exit(NULL);
}void *watch_count(void *t) {printf("启动 watch_count(): 线程 %ld\\n", (long)t);pthread_mutex_lock(&count_mutex);while (count < COUNT_LIMIT) {printf("watch_count(): 线程 %ld count= %d. 继续等待...\\n", (long)t, count);/* pthread_cond_wait 总是自动且原子地解锁互斥锁。*/pthread_cond_wait(&count_threshold_cv, &count_mutex);}count += 125;printf("watch_count(): 线程 %ld count = %d.\\n", (long)t, count);pthread_mutex_unlock(&count_mutex);pthread_exit(NULL);
}int main(int argc, char *argv[]) {int i, rc;pthread_t th[THRDS];pthread_attr_t attr;/* 初始化互斥锁和条件变量 */pthread_mutex_init(&count_mutex, NULL);pthread_cond_init (&count_threshold_cv, NULL);/* 为了兼容性,使用属性指明线程可被连接 */pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);pthread_create(&th[0], &attr, watch_count, (void *)1l);for(long i = 1; i < 3; i++) {pthread_create(&th[i], &attr, inc_count, (void *)i);}/* 等待所有线程结束 */for (i = 0; i < THRDS; i++) {pthread_join(th[i], NULL);}printf ("main():  等待并且连接了 %d 个线程. 最终 count = %d.\\n", THRDS, count);/* 清理并退出 */pthread_attr_destroy(&attr);pthread_mutex_destroy(&count_mutex);pthread_cond_destroy(&count_threshold_cv);pthread_exit (NULL);
}

输出:

启动 watch_count(): 线程 0
watch_count(): 线程 0 count= 0. 继续等待...
inc_count(): 线程 1, count = 1 
inc_count(): 线程 2, count = 2 
inc_count(): 线程 1, count = 3
inc_count(): 线程 2, count = 4 
inc_count(): 线程 1, count = 5 
inc_count(): 线程 2, count = 6 
inc_count(): 线程 1, count = 7 
inc_count(): 线程 2, count = 8 
inc_count(): 线程 1, count = 9 
inc_count(): 线程 2, count = 10 
inc_count(): 线程 1, count = 11 
inc_count(): 线程 2, count = 12  达到阈值.
inc_count(): 线程 2, count = 12
watch_count(): 线程 0 count = 137.
inc_count(): 线程 1, count = 138
inc_count(): 线程 2, count = 139
inc_count(): 线程 1, count = 140
inc_count(): 线程 2, count = 141
inc_count(): 线程 1, count = 142
inc_count(): 线程 2, count = 143
inc_count(): 线程 1, count = 144
inc_count(): 线程 2, count = 145
main():  等待并且连接了 3 个线程. 最终 count = 145.