对线程的控制思路和进程相似,创建、等待、终止,只需要调用接口就行。但是在Linux下没有线程的概念,因为Linux的设计者认为,线程是一种轻量级的进程,毕竟创建线程只需要创建PCB。因此Linux中使用多线程必须使用第三方pthread库,线程库为用户提供接口。
线程的创建——pthread_create
参数:
tread是线程标识符的指针,类似进程pid
attr是线程属性,一般是nullptr
start_routine是线程执行的函数
arg是传递给线程函数的参数
返回值:
线程的终止
线程终止有三种方式:
1.线程函数执行return,就会结束进程
2.线程函数使用pthread_exit接口
3.一个线程中使用pthread_cancel接口终止另一个进程
线程的等待——pthread_join
参数:
thread是线程标识符
retval是标识符对应线程退出后的返回值,是一个输出型参数。因为线程函数的返回值是void*类型,所以参数类型必须是void**
进程控制的例子:
#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
using namespace std;
//使用线程实现从a到b的累加
class Request
{
public:
int _start;
int _end;
string _threadname;
Request(int start, int end, string name)
:_start(start)
,_end(end)
,_threadname(name)
{}
};
class Response
{
public:
int _val;
int _exitcode;
Response(int val, int exitcode)
:_val(val)
,_exitcode(exitcode)
{}
};
void* cal(void* arg)
{
Request* rq = (Request*)arg;
Response* rsp = new Response(0, 0);
for(int i = rq->_start; i <= rq->_end; i++)
{
usleep(100000);
rsp->_val += i;
cout << rq->_threadname << " pid:" << getpid() << " operate" <<": ret += " << i << endl;
}
//线程间共用堆,把主线程的数据释放
delete rq;
return rsp;
}
int main()
{
pthread_t tid;
Request* rq = new Request(0,50,"mythread");
//创建线程
cout << "main thread pid:" << getpid() << " create thread" << endl;
pthread_create(&tid, nullptr, cal, (void*)rq);
void* ret;
//等待线程,获取结果
pthread_join(tid, &ret);
Response* rsp = (Response*)ret;
cout << rq->_threadname <<" cal ret = " << rsp->_val << " exitcode = " << rsp->_exitcode << endl;
delete rsp;
return 0;
}
使用线程库,编译时要用-lpthread选项,声明使用的库
通过指令看到线程的PID相同,因为它们都是同一个进程的执行流资源,LWP是线程标识符,不同线程互不相同
线程ID
我们知道LInux系统没有线程概念,线程这个概念是由线程库来维护的,线程库调用了系统调用接口clone
clone是创建进程的接口(fork的底层也使用了它),线程库对其封装,提供可以创建线程的接口。那么,线程库必然会对建立的所有线程进行管理,就像操作系统管理进程一样,创建对应的TCB等等。
线程库是一个动态库,进程运行时会加载到共享区。库中就有线程对应的数据结构,这些数据结构都被存储到一个数组中,数组中每个线程的数据结构的地址就是它的tid