从技术上讲,一个线程被定义为一个独立的指令流。
一个进程可以包含一个或多个线程。
线程操作包括线程创建,终止,同步(连接,阻塞),调度,数据管理和进程交互。
进程内的所有线程共享:
每个线程具有单独的:
POSIX 线程库是 C/C++ 的基于标准的线程API。
利用它我们可以操作线程,开发并行处理的程序。
一个简单的例子:
#include <pthread.h>
#include <stdio.h>
#define THRDS 5
void *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, // 线程 ID
const pthread_attr_t *attr, // 线程属性,NULL 则采用默认属性
void *(* start_routine)(void *), // 要线程化的函数的指针
void *arg); // 传递给 start_routine 函数的参数
线程函数的参数必须通过引用传递并转换为(void *)
。
若要传递多个参数,可创建一个包含所有参数的结构体,再传递指向该结构体的指针。
如果传递的参数是一个变量的地址,由于这是共享内存空间,变量对所有线程可见,很有可能在新线程访问它之前,此内存位置的值发生了更改。
pthread_exit
。pthread_cancel
取消。exec()
或 exit()
而终止。main()
先完成,且没有显式调用 pthread_exit
。如果没有显式地调用 pthread_exit()
, main()
就会在它产生的线程之前完成,那么所有线程都将终止。
显示调用 pthread_exit()
,则main()
会在结束前等待所有线程执行完毕。
我们也可以在 main()
中调用 pthread_join(t, NULL);
来连接子线程,连接后,当前线程就会阻塞并等待子线程 t 的结束。
另外创建时线程时可以通过线程属性指定是否可被连接。
Unix 的常见的线程同步机制:互斥(mutex)、信号量(semaphore)和条件变量(condition variable)。
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
类型,并且必须在可以使用它们之前进行初始化。有两种方法来初始化互斥变量:
pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
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 12
int 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);
}