线程是什么
线程是进程内部的一条执行序列(执行流),每个进程至少有一条执行序列:main的执行体。进程可以通过线程库创建N条线程,新建的线程为函数线程,main是主线程。虽然感觉是进程包含着线程,进程由线程组成,但是进程提出的概念比线程概念要早。
线程与进程的区别
1.进程是资源分配的最小单位,线程是CPU调度的最小单位。
2.线程是轻量级的进程
3.管理方式不一样。进程是pcb管理,线程是由线程结构管理。
线程的实现的三种方式
1、用户级 n:1关系
特点:用户管理复杂,内核管理简单
2、内核级 n:n
用户管理简单,内核管理复杂,cpu频繁中断,效率低
3、混合模式 n:m
我们现在使用的操作系统大部分为混合模式,内核级和用户级的优点和缺点都涵盖了,但是总体来说还是利大于弊。
线程的创建:
Int pthread_create(pthread_t *id,pthread_attr_t *attr,void* (*fun)(void *) ,void *arg);
功能:创建出一个函数线程。(编译时要链接库 gcc -o *** ***.c -lpthread)
第一个参数为标识符的地址。
第二个参数用来设置线程的属性,目前先写NULL就好
第三个参数为线程函数的地址,这个函数的返回值和参数都是void*类型的
第四个参数为第三个参数指针指向的函数的参数。这里的传参有两种方式,(都用整形的a来举例)
(1)将值强转成void*(不适用于float /double /struct /char *....)
参数里(void*)a 函数里int a=(int )arg;
2、将地址强转成void* (此种更适用多种情况)
参数里(void *)&a 函数里Int a=*(int *)arg;
注意:
创建出来的函数线程不同于函数调用,函数调用是这条执行流的一部分。
函数线程是创建出一条独立的执行序列,他与主线程同时执行。
int pthread_exit(void *);
功能:线程结束
参数可以设置线程结束状态,退出信息。
int pthread_join(pthread_t id,void **);
功能:等待线程结束
可以获取到等待的线程通过pthread_exit 设置的状态信息。
这三个函数我们用以下一系列的例子来说明它们的用处和用法
比如一个进程里main线程每秒打印一次main running 共 5次
创建的函数线程每秒打印一次pthread running 共 3次
结果貌似很正常。
但如果把两个线程的打印次数互换呢?即main3次,创建的线程5次。
我们可以看到创建的线程并没有完成工作,就随着main线程的结束而结束了。
这是因为main线程结束时,会默认的使用exit(0)退出整个进程,因此其他线程就因此也结束了。
那么怎么才能防止main线程退出整个进程呢?那就该用到 pthread_exit退出线程函数了,main最后使用这个退出线程,就不会再使用exit(0)了,其他线程也可以执行完毕。这里再强调一个使用习惯,就是每个线程的最后都最好加上这个退出线程函数。
这样pthread线程就打印了5次了。
可是如果不想让main线程结束呢?又想让pthread线程完成它的工作。那么就要用到等待函数了。
在main线程执行完3次打印后,暂停运行 等待pthread线程执行完毕后,main线程继续运行。
最后为了证明main线程还活着,加了句printf。
可以看到main线程仍在,pthread也完成了5次打印。
那么等待函数的那个参数 void**究竟是干什么用的呢?其实它和 退出函数的参数有关系,退出函数的参数不是一个void *吗?其实这个void **就是来记录 要等待的那个线程的退出时的一些信息(就是退出函数里的参数)。比如pthread线程执行完之后要使用退出函数,使用 pthread_exit("i finish my work!")里面那个字符串就是线程退出时想表达的信息,意思“我完成了我的工作”。有时线程可能不一定完成任务。我们会在条件判断一下,然后再逻辑判断的分支后使用退出函数 传入特定的出错参数,到时候main线程或者其他发起等待的线程就能通过这个参数得知这个子线程究竟完成任务与否,在决定如何进行下一步的。