LINUX系统编程:线程控制

目录

1.铺垫

2.验证

3.线程的等待

4.线程退出


1.铺垫

在LINUX内核中是不存在线程的概念的,只有轻量级进程的概念,但是在用户的角度来看,用户就是要把轻量级进程当做线程的使用,所以将轻量级进程的控制接口,封装成了一个线程库,用户直接使用库,也就屏蔽了底层的实现。

所以在g++编译的时候记得 加上 -lpthread选项,让可执行程序链接ptread库

2.验证

创建一个新的线程

参数thread,是输出型参数,创建进程的的id。

参数attr是一个包含创建时间,属性的结构体,用于初始化新创建的线程,设置为NULL采用默认初始化。

start_routine是一个函数指针,线程会执行函数的内容。

arg是start_routine的参数。

cpp 复制代码
#include <iostream>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
void *start_routine(void *v)
{
    while (true)
    {
        cout << "I am new thread pid: " << getpid() << endl;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, &start_routine, nullptr);
    while (true)
    {
        cout << "I am main thread pid:" << getpid() << endl;
        sleep(1);
    }

    return 0;
}

我们发现这两线程的pid是同一个,这是因为他们属于同一个进程。

那操作系统调度的时候怎么区分他们两个呢?

别忘了,进程和线程在liunx下都叫轻量级进程LWP

让我们使用 ps -aL 选项查看进程的所有轻量级进程。

我们发现有两个LWP449636,449637,其中449636就是main函数的执行流,也就是说当进程内部之有一个执行流的时候PID==LWP。

3.线程的等待

关联一个结束的进程

参数thread是线程的id,这个id来源自pthread库,而不是系统内核的LWP,他们的关系是一 一对应的。

参数retval是一个输出型参数,用于保存目标线程的退出值。

4.线程退出

线程退出是不可以使用exit的,因为exit是给进程用的,使用exit直接整个进程就干掉了

1.return可以退出线程。

终止调用的线程。

3.

终止参数thread线程

5.demo代码

v1

cpp 复制代码
#include <iostream>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
using namespace std;

// 创建5个进程,分别让他们执行任务,获取返回的结果

enum
{
    sum = 5
};

void *handler(void *arg)
{
    char* threadname = static_cast<char*>(arg);
    cout << "I am: " << threadname << endl;
    delete[] threadname;
    return (void *)1;
}

int main()
{
    // 创建线程
    vector<pthread_t> tids;
    for (int i = 0; i < sum; i++)
    {
        sleep(1);
        char *buffer = new char[64];
        pthread_t tid;
        snprintf(buffer, 64, "thread %d", i + 1);
        pthread_create(&tid, nullptr, handler, buffer);
        tids.push_back(tid);
    }

    for (auto &t : tids)
    {
        void *ret;
        int n = pthread_join(t, &ret);
        cout << "main thread get ret:" << (long long)ret << " " << "n:" << n << endl;
    }
    return 0;
}

v2

别忘了pthread_create最后一个参数类型是void* 这意味着可以和c++的类结合起来,不仅仅是传字符串,传整形,同样返回也可以使用类。

cpp 复制代码
#include <iostream>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>
#include <string>
#include <cstdio>
using namespace std;

// 创建5个进程,分别让他们执行任务,获取返回的结果

enum
{
    sum = 5
};
class result//储存结果
{
public:
    result()
    {
    }
    result(const string &threadname, int ret)
        : _threadname(threadname)
        , _ret(ret)
    {

    }
    void print()
    {
        cout << "I am " << _threadname << "added:" << _ret << endl;
    }

private:
    string _threadname;
    int _ret;
};

class doTask//执行任务
{
public:
    doTask()
    {
    }
    doTask(int x, int y)
        : _x(x), _y(y)
    {
    }

    int run()
    {
        return _x + _y;
    }

private:
    int _x;
    int _y;
};

class SentTask//发送任务
{
public:
    SentTask()
    {
    }
    SentTask(string &threadname, int x, int y)
        : _threadname(threadname)
        , dt(x, y)
    {
    }
    int Exute()
    {
        return dt.run();
    }

    string threadname()
    {
        return _threadname;
    }

private:
    string _threadname;
    doTask dt;
};

void *handler(void *args)
{
    SentTask *pst = static_cast<SentTask *>(args);
    result *ret = new result(pst->threadname(), pst->Exute());
    delete pst;
    return ret;
}

int main()
{
    vector<pthread_t> tids;
    for (int i = 0; i < sum; i++)
    {
        pthread_t tid;
        char buffer[64];
        snprintf(buffer, 64, "thread %d", i + 1);
        string threadname = buffer;
        SentTask *st = new SentTask(threadname, i, 3);
        pthread_create(&tid, nullptr, handler, st);
        tids.push_back(tid);
    }

    for (auto t : tids)
    {
        void *ret;
        pthread_join(t, &ret);
        result *tmp = static_cast<result *>(ret);
        tmp->print();
        delete tmp;
    }

    return 0;
}

5.补充

线程的优点

1.创建一个线程的代价比进程小的很多,创建一个线程就创建pcb,不用创建地址空间页表。

2.操作系统切换线程的工作,要比进程小的很多。如何理解呢?进程切换和线程切换相比,无非就是多了切换几个寄存器的工作呗,能有多大消耗呢?

但是别忘了还有缓存命中这种东西,cpu在加载代码的时候,会把该代码附近几行也加载到cpu的高速缓存中,一旦进程切换了,这些高速缓存的数据,对于下个进程就没有用了,都是失效的数据,但是对线程不是这样的,线程代码和全局数据是共享的。

线程的缺点

如果线程过多也是会有性能损失,因为线程来回切换也需要成本,在计算密集型应用线程数不要大于cpu核数。

相关推荐
yeyuningzi几秒前
Debian 12环境里部署nginx步骤记录
linux·运维·服务器
羊小猪~~3 分钟前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
上辈子杀猪这辈子学IT19 分钟前
【Zookeeper集群搭建】安装zookeeper、zookeeper集群配置、zookeeper启动与关闭、zookeeper的shell命令操作
linux·hadoop·zookeeper·centos·debian
minihuabei24 分钟前
linux centos 安装redis
linux·redis·centos
EasyCVR1 小时前
萤石设备视频接入平台EasyCVR多品牌摄像机视频平台海康ehome平台(ISUP)接入EasyCVR不在线如何排查?
运维·服务器·网络·人工智能·ffmpeg·音视频
脉牛杂德1 小时前
多项式加法——C语言
数据结构·c++·算法
legend_jz1 小时前
STL--哈希
c++·算法·哈希算法
CSUC1 小时前
【C++】父类参数有默认值时子类构造函数列表中可以省略该参数
c++
Vanranrr1 小时前
C++ QT
java·c++·qt
鸿儒5171 小时前
C++ lambda 匿名函数
开发语言·c++