Linux操作系统提供了pthread线程库,它是符合POSIX标准的函数库。线程控制方面的函数定义在pthread.h文件中,信号量控制方面的函数定义在semaphore.h文件中。
本文中包含以上大部分函数的基本用法,用于后期一道大程的设计和编写。其中信号量这一块不单独属于多线程编程部分,在此暂时先不讲。
参考书籍:unix高级编程第11章。
线程的ID: 用pthread_t的数据类型表示
ID比较:int pthread_equal(pthread_t tid1, pthread_t tid2)
ID获取:pthread_t pthread_self(void)
线程的创建:
int pthread_create(pthread_t restricttidp, const pthread_attr_t *restrict attr,void (start_rtn)(void), void *restrict arg);
其中,tidp指向的内存单元存储新创建线程的线程ID,attr先设置为null, 新创建的进程从start_rtn的函数地址开始运行,arg为需要传递的参数。
线程的等待:int pthread_join(pthread_t thread, void **rval_ptr);
线程的结束:int pthread_exit(void *rval_ptr);
线程的取消:int pthread_cancel(pthread_t tid);
一般线程的结束可以通过三种途径:线程执行完毕,线程被同一进程中的其他线程取消(pthread_cancel),自己调用pthread_exit。注意,pthread_cancel不等待线程终止,仅向线程提出取消请求。
再来解释一下rval_ptr参数。当线程1采用err = pthread_join(thread2, &res)等待线程2的时候,线程2调用pthread_exit((void *)1),此时res值即为exit返回的参数1。同时还可以通过这个指针来传递复杂的结构体,但必须注意在传递回线程1后内存仍必须是有效的。
下面的程序用多线程实现斐波那契数列的计算。其中子线程用于计算斐波那契数列中每一个项的值,主线程负责输出。
#include
#include
#include
#include
pthread_t thread_id;
int num;
void *thread_calc(int arr[])
{
for (int i=2; i<=num; i++)
arr[i] = arr[i-1] + arr[i-2];
return NULL;
pthread_exit(NULL);
}
int main()
{
int* arr;
scanf("%d", &num);
arr = (int*)malloc(sizeof(int) *(num+1));
arr[0] = 0;
arr[1] = 1;
int res = pthread_create(&thread_id, NULL, thread_calc, arr);
if (res == -1) printf("Thread create failed!");
printf("Waiting for the thread to compute!\n");
pthread_join(thread_id, NULL);
for (int i=0; i<=num; i++)
printf("array[%d] = %d\n",i,arr[i]);
}
线程的同步主要通过互斥变量来完成。简单的来说就是跟数据库里一样,对这个程序正在进行读写的资源加锁,其他任意想要访问这个资源的线程都会被阻塞。互斥变量用pthread_mutex_t数据类型来表示,初始化方式有两种:
同时如果动态分配互斥量(malloc),需要用pthread_mutex_destory(pthread_mutex_t *mutex)来释放内存。
对互斥量加锁解锁的函数为以下三个:
#include
#include
#include
//unistd.h 是 C 和 C++ 程序设计语言中提供对 POSIX 操作系统 API 的访问功能的头文件的名称。
#include
void* thread_add();
void* thread_sub();
pthread_t th_add, th_sub;
pthread_mutex_t lock;
int global_count = 0;
int main()
{
int res;
res = pthread_mutex_init(&lock, NULL);
if (res == -1) {
printf("Create mutex failed!\n");
exit(1);
}
res = pthread_create(&th_add, NULL, thread_add, NULL);
if (res == -1) {
printf("Create thread failed!\n");
exit(1);
}
res = pthread_create(&th_sub, NULL, thread_sub, NULL);
if (res == -1) {
printf("Create thread failed!\n");
exit(1);
}
pthread_join(th_add, NULL);
pthread_join(th_sub, NULL);
}
void* thread_add()
{
while (global_count < 20) {
sleep(2);
pthread_mutex_lock(&lock);
printf("Before add: %d\n", global_count);
global_count += 5;
printf("After add: %d\n", global_count);
pthread_mutex_unlock(&lock);
}
pthread_exit(NULL);
}
void* thread_sub()
{
while (global_count > -20) {
sleep(1);
pthread_mutex_lock(&lock);
printf("Before sub: %d\n", global_count);
global_count -= 5;
printf("After sub: %d\n", global_count);
pthread_mutex_unlock(&lock);
}
pthread_exit(NULL);
}
如果一段程序有种两个互斥量(锁)的时候,就很容易出现死锁的情况。死锁为线程1锁住资源a,同时正在请求资源b,同时线程2锁住资源b,同时正在请求资源a。该情况复杂的多,具体实例可以详见unix高级编程第11章6.2。
条件变量的数据类型:
pthread_cond__t
条件变量的创建:
intpthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr,);
条件变量的反初始化:
intpthread_cond_destroy(pthread_cond_t *cond);
主要的三个函数:
int pthread_cond_wait(pthread_cond_t *restrict bond, pthread_mutex_t *restrict mutex);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
其中,在调用wait的时候,需要将互斥量一起传给函数。函数在收到互斥量并开始等待后,会先将互斥量解锁,方便其他线程先运行。signal和broadcast用于通知wait,其中signal通知一个及以上,broadcast通知所有的等待在消息队列中的条件。