1.概念
线程:在一个程序中的多个执行路线就叫做线程,更准确的定义:线程是一个进程内部的一个控制序列。
线程的优点:让程序并行执行;改善程序性能;线程的切换需要操作系统做的工作较少,因此多个线程对资源的需要远小于多个进程。
线程的缺点:多线程程序设计复杂;多线程程序高度难度较大;对于大量计算且在单核计算机线程运行的效率不高。
编写多线程程序需要包含头文件pthread.h,并且在编译程序时需要用选项-lpthread链接线程库。除此之外还需要定义宏_REENTRANT告诉编译器程序需要可重入功能。
可重入:可以被多次调用而仍然正常工作。
编译和链接:gcc -D_REENTRANT -I/usr/include/nptl -o xxx xxx.c -L/usr/lib/nptl -lpthread
若系统默认使用了上诉的库,可以直接:gcc -o xxx xxx.c -lpthread
2.创建一个新进程
#include<pthread.h>
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
第一个参数时指向 pthread_t 类型数据的指针,线程被创建时,这个指针指向的变量中将被写入一个标识符,我们用该标识符来引用新线程。
下一个参数用于设置线程的属性,一般设置NULL。第三个参数传递一个函数的地址,最后一个参数是传递给该函数的参数。
函数调用成功返回0,失败返回错误代码。
3.终止线程
#include<pthread.h>
int pthread_exit(void *retval);
终止调用它的线程并返回一个指向某个对象的指针。
4.join
#include<pthread>
int pthread_join(pthread_t th, void **thread_return);
等价于进程中用来收集子进程信息的wait函数。第一个参数是上面提到的标识符,第二个参数是指向另一个指针的指针,另一个指针指向线程的返回值。
5.同时执行
除局部变量外,所有其他变量都将在一个进程中的所有线程之间共享
6.同步
更好的控制线程执行和访问代码临界区域的方法:
a.信号量:分为二进制信号量(保护一段代码使其只能被一个执行线程运行)和计数信号量(允许有限数目的线程执行一段指定的代码)。
b.创建信号量
#include<semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
函数初始化由sem指向的信号量对象,设置它的共享选项,并给他一个初始值value(一般为0),第二个参数控制信号量类型(0表示局部信号量,否则可以表示共享)。
c.控制信号量
#include<semaphore.h>
int sem_wait(sem_t * sem);//给信号量值减一
int sem_post(sem_t * sem);//给信号量值加一
//都是原子操作(无冲突)
d.信号量清理
#include<semaphore.h>
int sem_destroy(sem_t * sem);
清理该信号量所有资源
7.用互斥量进行同步
互斥量允许程序锁住某个对象,使得每次只能有一个线程访问它。为了控制关键代码的访问,必须在进入代码段之前锁住一个互斥量,然后在完成操作后解锁。
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//上述函数调用执行成功时返回0,失败时返回错误代码,但是不设置errno
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *thread_function(void *arg);
pthread_mutex_t work_mutex; /* protects both work_area and time_to_exit */
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit = 0;
int time_to_exit = 0;
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_mutex_init(&work_mutex, NULL);
if (res != 0) {
perror("Mutex initialization failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&work_mutex);
printf("Input some text. Enter 'end' to finish\n");
while(!time_to_exit) {
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
while(1) {
pthread_mutex_lock(&work_mutex);
if (work_area[0] != '\0') {
pthread_mutex_unlock(&work_mutex);
sleep(1);
}
else {
break;
}
}
}
pthread_mutex_unlock(&work_mutex);
printf("\nWaiting for thread to finish...\n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined\n");
pthread_mutex_destroy(&work_mutex);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
sleep(1);
pthread_mutex_lock(&work_mutex);
while(strncmp("end", work_area, 3) != 0) {
printf("You input %d characters\n", strlen(work_area) -1);
work_area[0] = '\0';
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
while (work_area[0] == '\0' ) {
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
}
}
time_to_exit = 1;
work_area[0] = '\0';
pthread_mutex_unlock(&work_mutex);
pthread_exit(0);
}
8. 线程的属性
在创建线程之前可以通过设置线程的发生改变所创建线程的行为。线程有很多属性可以设置,比如可取消的状态、可取消类型、并发度,通过改变这些属性可以更精确地控制线程的运行。
#include<pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
初始化一个线程属性对象。还有一个pthread_attr_destroy 是对属性对象进行清理和回收。
9.取消线程
一个线程可以要求另一个线程终止,可能通过pthread_cancel实现。接收到终止消息的线程可以调用pthread_setcancelstate设置自己的取消状态。如果取消请求被接受,线程可以调用pthread_setcanceltype设置取消类型。取消类型有两种,PTHREAD_CANCEL_ASYNCHRONOUS使得在接收到取消请求后立即采取行动,PTHREAD_CANCEL_DEFERRED使得在接收到取消请求后,一直等待直到线程执行了pthread_join、pthread_cond_wait、pthread_cond_timewait、pthread_testcancel、sem_wait、sigwait中的任意一个函数后才采取行动。
#include <pthread.h>
int pthread_cancel(pthread_t thread);
int pthread_setcancelstate(int state, int *oldstate);//设置取消状态
int pthread_setcanceltype(int type, int *oldtype);//设置取消类型