线程控制

对线程的控制思路和进程相似,创建、等待、终止,只需要调用接口就行。但是在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**


分离线程------pthread_detach

如果说线程的返回值我们不关心,使用join对操作系统是一种负担,但是不等待线程也会造成内存泄漏。使用这个接口就不用等待线程,在线程执行完自动回收。

分离线程既可以在其他线程分离,也可以自己分离

其他线程传入要分离的线程ID,自己分离调用pthread_self()获取线程tid即可


进程控制的例子:

cpp 复制代码
#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

从上面的动态库结构看到:线程有自己独立的栈和局部存储。


线程栈

独立性

线程栈相互独立,也就是说每个线程即使使用了相同的线程函数,创建的变量也是互不相同的。

cpp 复制代码
#include <iostream>
#include <pthread.h>
#include <vector>

#define NUM 4//线程数量
using namespace std;

void* fun(void* arg)
{
    int val = 10;
    return (void*)&val;//返回栈中变量地址
}

int main()
{   
    vector<pthread_t> tids;
    //创建多个线程
    for(int i = 0; i < NUM; i++)
    {
        pthread_t tid;
        pthread_create(&tid, nullptr, fun, nullptr);
        tids.push_back(tid);
    }
    //查看栈中变量地址
    for(auto e : tids)
    {
        void* ret;
        pthread_join(e, &ret);
        cout << (int*)ret << endl;
    }
    return 0;
}

可见性

虽然栈是相互独立的,但是并不意味着栈中的数据对其他线程是不可访问的(实际应用中不推荐这种访问)

cpp 复制代码
#include <iostream>
#include <pthread.h>
#include <vector>
#include <unistd.h>
using namespace std;
int* addr;
void* fun(void* arg)
{
    int val = 0;
    addr = &val;
    int cnt = 10;
    //循环打印val
    while(cnt--)
    {
        sleep(1);
        cout << "val:" << val <<endl;
    }
    return nullptr;
}
int main()
{
    //创建线程
    pthread_t tid;
    pthread_create(&tid, nullptr, fun, nullptr);
    //修改val
    sleep(4);
    cout << "main change val: 10" << endl;
    *addr = 10;
    pthread_join(tid, nullptr);
    return 0;
}

线程局部存储

一个进程的全局变量对所有的线程都是可见的,如果想要一个线程独有的全局变量,可以使用线程局部存储。

在全局变量定义的前面加上 __thread

cpp 复制代码
#include <iostream>
#include <pthread.h>
#include <vector>
#include <unistd.h>

using namespace std;
__thread int gval = 0;

void *fun1(void *arg)
{
    gval += 100;
    cout << &gval << ' ' << gval << endl;
    return nullptr;
}

void *fun2(void *arg)
{
    gval += 200;
    cout << &gval << ' ' << gval << endl;
    return nullptr;
}
int main()
{
    vector<pthread_t> tids;
    pthread_t tid;
    pthread_create(&tid, nullptr, fun1, nullptr);
    tids.push_back(tid);
    pthread_create(&tid, nullptr, fun2, nullptr);
    tids.push_back(tid);

    for (auto e : tids)
    {
        pthread_join(e, nullptr);
    }
    return 0;
}
相关推荐
Xinan_____3 分钟前
Linux——高流量 高并发(访问场景) 高可用(架构要求)
linux·运维·架构
周伯通*13 分钟前
Windows上,使用远程桌面连接Ubuntu
linux·windows·ubuntu
Spring-wind33 分钟前
【linux】 date命令
linux
钡铼技术物联网关39 分钟前
Codesys 与 ARMxy ARM 工业控制器:工业控制的黄金组合
linux·运维·服务器·arm开发·硬件工程
Dola_Pan3 小时前
Linux文件IO(一)-open使用详解
java·linux·dubbo
Spring-wind3 小时前
【linux】pwd命令
linux
ken_coding4 小时前
Windows11 WSL2的ubuntu 22.04中拉取镜像报错
linux·ubuntu·docker
阳光开朗_大男孩儿4 小时前
DBUS属性原理
linux·服务器·前端·数据库·qt
gopher95114 小时前
linux驱动开发-设备树
linux·驱动开发
学习3人组5 小时前
克隆centos网卡uuid相同如何修改
linux·运维·centos