一个进程在同一时刻只做一件事情。有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务。进程是程序执行时的一个实例,是担当分配系统资源的基本单位。在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉会导致整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。与进程相比,线程相对来说比较简约,因为其是其他线程共享数据段,而且通信的机制也相对将简单。
多线程编程所需要的库,在linux上已经有pthread库支持,编程最基本包括了三点:线程的操作,互斥锁,条件。
#include<pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg); void pthread_exit(void *retval); int pthread_join(pthread_t thread, void **retval);
①pthread_create:线程创建
thread: 是线程ID,当create成功后,ID号也被设置成功
attr:线程属性,一般可以设置NULL,以设置默认属性
start_routine:线程函数指针,参数类型是无类型指针
arg:是传递给线程的参数,如果想传多组数据,可以定义一个结构体
②pthread_join:线程等待,一直阻塞直到目标线程退出
thread:要等待的线程的ID
retval:会包含线程的返回码,如果不关心返回的状态可以设置为NULL
③pthread_exit:线程退出
retval:退出码
这里可以举个例子,注意这里编译时需要链库,-lpthread
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> void *func2(void * data){ printf("in the thread2\n"); sleep(2); printf("func2 quit!!!\n"); pthread_exit(0); } void *func1(void * data){ printf("in the thread1\n"); printf("传递的参数是:%d\n",*(int *)data); printf("线程1 ID:%ld\n",pthread_self()); sleep(2); printf("func1 quit!!!\n"); pthread_exit(0); } int main(){ pthread_t t1; pthread_t t2; int data=20; int *ret; pthread_create(&t1,NULL,func1,(void *)&data); pthread_create(&t2,NULL,func2,(void *)&data); printf("main:%ld\n",pthread_self()); pthread_join(t1,(void*)&ret); pthread_join(t2,(void*)&ret); return 0; }
#include<pthread.h> int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); int pthread_mutex_destroy(pthread_mutex_t *mutex); int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex);
①pthread_mutex_init:创建互斥锁,动态创建
mutex互斥锁名称。
attr是互斥锁的属性,这里可以设置NULL为默认属性
如果想使用静态创建的方法,可以用一个宏PTHREAD_MUTEX_INITIALIZER,然后定义一个互斥锁为这个宏就可以不使用init函数
②pthread_mutex_destroy:销毁互斥锁
③pthread_mutex_lock:加互斥锁
④pthread_mutex_unlock:解互斥锁
⑤pthread_mutex_trylock:尝试解互斥锁
mutex是需要进行操作的互斥锁,当一个线程取到了锁,在解锁之前,另一个线程试图调用lock上锁,就会阻塞在那里。而如果另一个线程调用的不是lock而是trylock,这时会判断互斥量是否锁住,如果没有则锁住,如果上锁了,则上锁失败,但不会阻塞
何为死锁,当一个程序在运行的过程中,在多线程任务中,一个线程在对一个互斥量进行上锁后,在没有解锁该互斥量前,还准备对另一个互斥量进行上锁,而此时这个互斥量正在被第二个线程上锁,并且未解锁,这第二个线程也想对第一个线程中已上锁的互斥量进行上锁,就会两个线程同时阻塞在进程中,这时用trylock可以解决死锁。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> pthread_mutex_t mutex1; pthread_mutex_t mutex2; void *func3(void * data){ printf("in the thread3\n"); pthread_mutex_lock(&mutex1); sleep(1); pthread_mutex_trylock(&mutex2); printf("func3 arrive!!!\n"); pthread_mutex_unlock(&mutex1); pthread_mutex_unlock(&mutex2); } void *func2(void * data){ printf("in the thread2\n"); pthread_mutex_lock(&mutex2); sleep(1); pthread_mutex_trylock(&mutex1); printf("func2 arrive!!!\n"); pthread_mutex_unlock(&mutex2); pthread_mutex_unlock(&mutex1); } void *func1(void * data){ printf("in the thread1\n"); printf("传递的参数是:%d\n",*(int *)data); } int main(){ pthread_t t1; pthread_t t2; pthread_t t3; int data=20; int *ret; pthread_mutex_init(&mutex1,NULL); pthread_mutex_init(&mutex2,NULL); pthread_create(&t1,NULL,func1,(void *)&data); pthread_create(&t2,NULL,func2,(void *)&data); pthread_create(&t3,NULL,func3,(void *)&data); printf("main:%ld\n",pthread_self()); pthread_join(t1,(void*)&ret); pthread_join(t2,(void*)&ret); pthread_join(t3,(void*)&ret); pthread_mutex_destroy(&mutex1); pthread_mutex_destroy(&mutex2); return 0; }
从代码可以看到,这里准备是想强制让其发生死锁的现象,但是因为用的是trylock,所以线程并不会阻塞
#include <pthread.h> int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); int pthread_cond_destroy(pthread_cond_t *cond); 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, cond struct timespec *restrict ti int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond);
①pthread_cond_init:创建条件变量,动态创建
cond:条件变量名,attr条件的属性,这里依旧可以设置为NULL以默认属性来创建
也可以用一个宏PTHREAD_COND_INITIALIZER来静态创建,当使用了这个宏后,init函数就可以不需要了
②pthread_cond_destroy:销毁条件变量
③pthread_cond_wait:等待发送另一个线程发送信号,否则该线程阻塞
④pthread_cond_timedwait:与wait类似,但是有一个定时time
⑤pthread_cond_signal:唤醒满足该条件的某个线程
⑥pthread_cond_broadcast:唤醒满足该条件的所有线程,如果要唤醒所有线程,那么正在等待的线程中的互斥锁需要都不一样,否则和signal函数的效果一样,只能唤醒单个线程。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> pthread_mutex_t mutex1; pthread_mutex_t mutex2; pthread_cond_t cond; void *func3(void * data){ printf("in the thread3\n"); pthread_cond_wait(&cond,&mutex1); printf("func3 arrive!!!\n"); } void *func2(void * data){ printf("in the thread2\n"); pthread_cond_wait(&cond,&mutex2); printf("func2 arrive!!!\n"); } void *func1(void * data){ printf("in the thread1\n"); printf("传递的参数是:%d\n",*(int *)data); printf("线程1:%ld\n",pthread_self()); pthread_mutex_lock(&mutex1); pthread_mutex_lock(&mutex2); sleep(3); pthread_cond_broadcast(&cond); pthread_mutex_unlock(&mutex1); pthread_mutex_unlock(&mutex2); } int main(){ pthread_t t1; pthread_t t2; pthread_t t3; int data=20; int *ret; pthread_mutex_init(&mutex1,NULL); pthread_mutex_init(&mutex2,NULL); pthread_cond_init(&cond,NULL); pthread_create(&t1,NULL,func1,(void *)&data); pthread_create(&t2,NULL,func2,(void *)&data); pthread_create(&t3,NULL,func3,(void *)&data); printf("main:%ld\n",pthread_self()); pthread_join(t1,(void*)&ret); pthread_join(t2,(void*)&ret); pthread_join(t3,(void*)&ret); pthread_mutex_destroy(&mutex1); pthread_mutex_destroy(&mutex2); pthread_cond_destroy(&cond); return 0; }
这里可以看到当线程一中有两个互斥锁,两个待唤醒线程中对应的互斥锁也不一样,所以才能同时一起唤醒