计算机是完成特定任务的工具,我们把特定的任务称为task。最早的计算机是单task系统,需要手动装载,启动task的程序,所有的资源由该task独占,一个task完成后才能启动下一个task。操作系统的一个重要功能是管理task,为task分配系统资源。进程是操作系统分配资源和调度task的基本单位。
进程是一个具有一定独特功能的程序在一个数据集合上的一次动态执行。进程由代码块(静态,二进制),数据集,进程控制块(动态)三部分组成。进程概念的引入是为了便于理解操作系统对task管理。进程是操作系统为task分配系统资源的基本单位,也是task调度的基本单位。
线程是CPU计算流的抽象,一般认为它由程序计数器(PC),栈和寄存器组构成。从操作系统的角度来看,线程使资源和计算流分离,多个线程可以共享系统资源,实现了系统资源(内存,文件,信号)的高效利用。同时,这种共享也实现了计算流之间更高效的交互,实现更高的并行和并发度。
POSIX的使用
demo
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 | #include <sys/types.h>#include <unistd.h>#include <pthread.h>// control the access of shared_resourcepthread_mutex_t resource_mutex;pthread_cond_t resource_cond;int shared_resource = 0;void* (void* args){ pthread_t* ptid = (pthread_t*) malloc(sizeof(*ptid)); pid_t pid = *(pid_t*)args; // get thread id *ptid = pthread_self(); printf("thread %d start, created by process %dn", *ptid, pid); // wait for certain condition to happen: shared_resource == 1 pthread_mutex_lock(&resource_mutex); while (shared_resource != 1) { pthread_cond_wait(&resource_cond, &resource_mutex); } printf("thread %d access shared_resource: %dn", *ptid, shared_resource); pthread_mutex_unlock(&resource_mutex); // thread exit pthread_exit(ptid);}void* thread_worker2(void* args){ pthread_t* ptid = (pthread_t*) malloc(sizeof(*ptid)); pid_t pid = *(pid_t*)args; // get thread id pthread_t tid = pthread_self(); printf("thread %d start, created by process %dn", *ptid, pid); // access shared resource pthread_mutex_lock(&resource_mutex); printf("thread %d access shared_resource: %dn", *ptid, shared_resource); shared_resource = 1; pthread_mutex_unlock(&resource_mutex); // wake up thread who is waitting on resource_cond pthread_cond_signal(&resource_cond); pthread_exit(NULL);}int main(){ pthread_t tid1, tid2; // initialize mutex and condition variables pthread_mutex_init(&resource_mutex, NULL); pthread_cond_init(&resource_cond, NULL); pid_t pid = getpid(); // create work threads pthread_create(&tid1, NULL, thread_worker1, (void*)&pid); pthread_create(&tid2, NULL, thread_worker2, (void*)&pid); // wait for th 大专栏 进程和线程read to join pthread_t* result; pthread_join(tid1, (void*)&result); printf("%d return by thread %dn", *result, tid1); free(result); pthread_join(tid2, NULL); // destory mutex and condition variables pthread_mutex_destroy(&resource_mutex); pthread_cond_destroy(&resource_cond); return 0;} |
互斥锁
互斥锁是为了保证在同一时刻只有一个线程操作(读写)共享资源。POSIX API有以下几个:
123456789101112131415161718192021222324252627282930313233343536 | /** * 声明互斥锁 **/pthread_mutex_t mutex;/** * 初始化互斥锁 * 静态方法:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; * 动态方法:int pthread_mutex_init(pthread_mutex_t *, * const pthread_mutexattr_t *); **/pthread_mutex_init(&mutex, NULL);/** * 获取锁 * int pthread_mutex_lock(pthread_mutex_t *); **/pthread_mutex_lock(&mutex);/** * 释放锁 * int pthread_mutex_unlock(pthread_mutex_t *); **/pthread_mutex_unlock(&mutex);/** * 尝试获取锁,失败时不阻塞 * int pthread_mutex_trylock(pthread_mutex_t *); **/pthread_mutex_trylock(&mutex);/** * 销毁锁 * int pthread_mutex_destroy(pthread_mutex_t); **/pthread_mutex_destroy(&mutex); |
条件变量
条件变量用于同步,即在达到某种条件时线程才继续运行,否则挂起等待,直到满足条件后被唤醒。线程一般通过检查某个共享变量的值来确定是否要挂起等待,所以条件变量一般与互斥锁结合实现同步机制。条件变量相关的 POSIX API如下:
123456789101112131415161718192021222324252627282930313233343536 | int status = 0;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_t cond;/** * 初始化 * 静态方法:pthread_cond_t cond = PTHREAD_COND_INITIALIZER; * 动态方法:int pthread_cond_init(pthread_cond_t*); ** / pthread_cond_init(&cond); pthread_mutex_lock(&mutex); while (status != 1) {/** * 挂起等待,如果条件不成立 * int pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*); ** / pthread_cond_wait(&cond, &mutex); } pthread_mutex_unlock(&mutex);/** * 唤醒等待线程 * 全部线程:int pthread_cond_broadcast(pthread_cond_t*); * 单个线程:int pthread_cond_signal(pthread_cond_t*); **/pthread_cond_signal(&cond);pthread_cond_broadcast(&cond);/** * 销毁 * int pthread_cond_destroy(pthread_cond_t*); **/pthread_cond_destroy(&cond);ptread_mutex_destroy(&mutex); |
这里需要主要的是,一个条件变量同一时刻只能与一个互斥量配套使用,而一个互斥量可以同时与多个条件变量对应。
线程模型
逻辑上,一个进程对应一组线程,这是一个典型的一对多的模型。但是从实现上,又有几种不同的模型,常见的有一对一,一对多,多对多几种。
在介绍线程模型前,必须清楚两个概念:用户级线程(ULT)和内核级线程(KLT)。ULT,顾名思义,运行于用户态的线程,对操作系统的内核是不可见的,用户自行管理线程的调度。内核级线程,运行于内核态的线程,内核可见,内核参与线程调度。
一对一
一个KLT对应一个ULT,早期的POSIX实现版本Linux Threads就是根据这个模型实现的。它利用Linux的轻量级进程(LWP,通过clone()系统调用)实现不同进程间的资源共享。有专门的管理线程负责线程调度。
多对一
一个KLT对应多个ULT。缺点是:如果一个线程在系统调用上阻塞,整个进程阻塞。
多对多
多个KLT对应多个ULT,一对一和一对多的结合,实现更高的并行性。IBM的NGPT是基于这一模型实现的。
来源:https://www.cnblogs.com/lijianming180/p/12286189.html