概念:
(1)PCB(Process Control Block)进程管理块:系统中存放进程的管理和控制信息的数据结构体,每一个进程均有一个PCB,在创建进程时建立,直到进程撤销而撤销。
(2)程序段:是进程中能被进程调度程序在CPU上执行的程序代码段。
(3)数据段:一个进程的数据段,可以是进程对应的程序加工处理的原始数据,也可以是程序执行后产生的中间或最终数据。
从结构上看,进程由程序段、数据段和PCB三部分组成。
(4)进程:进程是正在运行的程序的实例,是系统进行资源分配和调度的基本单位。
(5)线程:线程是操作系统能够进行运算调度的最小单位,被包含在进程里面,是进程中的实际运作单位,可以与同属一个进程的其他线程共享进程所拥有的全部资源。
线程是进程的一部分,一个线程只能属于一个进程,而一个进程可以有多个线程,并且至少有一个线程。
进程是操作系统资源分配的基本单位,线程是任务调度和执行的基本单位。
linux系统原本没有线程,后来受windows多线程编程影响,做出了类似windows线程的Linux版本的线程,但是归根结底还是进程,只不过是轻量及的进程,开销会比真正的进程要小的多。
实现:增加线程后,线程均分进程原有的PCB,原先的进程退化为主线程。但是PCB占用总和并没有变,所以并没有提升效率,对于内核而言,线程依旧是进程,但是线程间通信共享资源等会方便的多。
---------------------------------------------------------------------------------------------------
Linux下编写C程序,头文件中包含pthread.h,但是报错:对 'pthread_create'未定义的引用。
原因:phthread库不是linux系统的默认库,连接时需要使用libpthread.a库,编译时需要写:gcc test.c -lpthread
--------------------------------------------------------------------------------------------------
创建线程:int pthread_create(pthread_t *pthread, const pthread_attr_t *attr, void *(start_routine)(void*), void *arg)
pthread:返回线程ID。返回的是新创建的线程的ID
attr:设置线程的属性。一般为NULL,设置的是新线程的属性。
start_routine:函数地址。线程启动后执行的函数,是一个函数指针,指向线程的入口函数
arg:传给线程启动函数的参数。
函数返回值:成功返回0,失败返回错误码。
线程创建后,并不能保证哪个线程先执行,新创建的线程和调用线程的执行顺序不确定,由操作系统进行调度。
pthread_self():得到当前线程的ID。
终止线程:
(1)pthread_exit和pthread_join
void pthread_exit(void *retval),retval是一个空指针,用来保存线程退出之后的返回值,成功返回0,失败返回错误码。这个指针必须是全局的或者被malloc分配的,当线程得到这个返回指针时程序已经退出
int pthread_join(pthread_t thread, void **retval) :thread是线程指针,retval是用户定义的指针,用来存储被等待线程的返回值。
使用:在线程中调用pthread_exit()函数,在主线程中在调用pthread_create()函数启动线程后,调用pthread_join()函数,等待线程结束。
(2)取消一个执行中的线程:int pthread_cancel(pthread_t thread),成功返回0,失败返回错误码,thread是线程ID
和phread_join结合使用:(作用与进程中的kill()函数对应)
(3)只终止某个线程,而不终止整个进程:从需要终止的函数return。此方式对主线程不适用,从main函数return相当于exit。
获取当前线程ID的函数:phtread_t pthread_self(void)
获取当前文件中国线程tid的命令:ps -eLf | head -1&& ps -eLf | grep a.out
-------------------------------------------------------------------------------------
互斥锁:保证共享数据的完整性。
pthread_mutex_t:互斥变量,一个锁类型的结构体。保证任一时刻,只有一个线程来访问对象。
(1)创建互斥锁:pthread_mutex_t mtx;
(2)互斥锁初始化:
<1> 动态方式:int pthread_mutex_init( pthread_mutex_t *mutex, const pthread_mutexattr_t *mattr)
mutex:互斥锁;mattr:互斥锁属性,为空使用缺省值。函数成功返回0,执行成功后,互斥锁被初始化锁住状。
<2> 静态方式:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
(3)加锁
<1> 阻塞方式:int pthread_mutex_lock(pthread_mutex_t *mutex)
如果参数mutex所指的互斥体已经被锁住了,那么发出调用的线程将被阻塞直到其他线程对mutex解锁
<2> 非阻塞方式:int pthread_mutex_trylock(pthread *mutex)
如果参数mutex所指的互斥锁已经被锁住,发出调用的线程不会阻塞等待,而会返回一个错误码
(4)解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex)
(5)销毁锁
int pthread_mutex_destroy(pthread_mutex_t *mutex)
-------------------------------------------------------------------------------------
练习:
<1>
问题1:为什么主函数中不调用pthread_join()函数,调用的线程就没有输出?
原因:pthread_join()函数以阻塞的方式使一个线程来等待另一个线程的结束。代码中若没有pthread_join()函数,主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会执行就结束了。主线程调用pthread_join()之后,就会等待函数等待的线程结束后再退出。
mian函数是一个进程,当main函数里创建线程之后,main函数从进程退步为主线程。
<2>互斥锁的使用
在线程调用的程序段前后加解锁之后,线程会排队调用程序段。