IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 多线编程与资源同步(学习笔记) -> 正文阅读

[C++知识库]多线编程与资源同步(学习笔记)

多线编程与资源同步

在Windows下,主线程退出后,子线程也会被关闭;

在Linux下,主线程退出后,系统不会关闭子线程,这样就产生了僵尸进程

3.2.1创建线程

  • Linux 线程的创建

    #include <unistd.h>
    #include <stdio.h>
    #include <pthread.h>
    void* threadfunc(void* arg)
    {
        while (1)
        {
            sleep(1);
            printf("I am a new thread!!!\n");
        }
        return NULL;
    }
    
    int main()
    {
        pthread_t threadid;
        pthread_create(&threadid, NULL,threadfunc, NULL);
        while (1)
        {
    
        }
        return 0;
    }
    
  • Windows CRT1提供的线程创建函数

    #include <process.h>
    #include <stdio.h>
    unsigned int __stdcall threadfun(void* args)
    {
    	while (true)
    	{
    		printf("I am new thread!");
    	}
    	return 0;
    }
    int main(int argc,char* argv[])
    {
    	unsigned int threadid;
    	_beginthreadex(0, 0, threadfun, 0, 0, &threadid);
    
    	while (true)//不让主线程退出
    	{
    
    	}
    	return 0;
    }
    
  • C++ 提供的std::thread

    #include <iostream>
    #include <thread>
    
    void threadproc1()
    {
    	while (true)
    	{
    		printf("I am aNew Thread!!");
    	}
    }
    
    void threadproc2(int a,int b)
    {
    	while (true)
    	{
    		printf("I an Thread2");
    	}
    }
    
    int main()
    {
    	std::thread t1(threadproc1);
    	std::thread t2(threadproc2, 1, 2);
    	while (true)
    	{
    		
    	}
    }
    

    这种方法容易出错,原因如下:

    #include <iostream>
    #include <thread>
    
    void threadproc1()
    {
    	while (true)
    	{
    		printf("I am aNew Thread!!");
    	}
    }
    
    void func()
    {
    	std::thread t(threadproc1);
    }
    int main()
    {
    	func();
    	while (true)
    	{
    		
    	}
    }
    

    这段代码实际上试运行不了的,在vs2019 release模式下结果:

    image-20210703231616070

    在func函数调用完成后,func中的局部变量t被销毁,而此时线程函数仍然在运行.所以使用std::thread创建线程必须保证线程函数运行期间,线程对象始终有效!!

    当然,我们也可以使用detach方法解决这个问题,使线程函数和线程对象脱离

    ...
    void func()
    {
    	std::thread t(threadproc1);
        t.detach();
    }
    ...
    

    这样就可以运行了,但是不推进这样做,因为我们需要线程对象对线程进行管理

    3.2.2线程ID

    下面介绍一下Linux系统线程ID本质

  • Linux系统线程ID本质

    在Linux系统中有三种方法可以获取一个线程的ID

    • 调用pthread_create函数时,可以通过第一个参数获取线程ID
    • 在需要获取ID的线程中调用pthread_self函数获取
    • 通过系统调用获取线程ID

    其中,方法一和方法二获取线程ID的结果都是一样的,都是pthread_t类型

    image-20210703234233186

不同的进程可能有同样的地址内存块(共享内存),所以通过方法一和方法二获取到的线程ID可能不是全系统唯一的,而方法三获取的线程ID是全系统唯一的,就是LWP(轻量级进程)2

  • C++ 获取线程ID的方法

    #include <thread>
    #include <iostream>
    #include <sstream>
    void worker_thread_func()
    {
        while (true)
        {
    
        }
    }
    
    int main()
    {
        //获取线程t的id
        std::thread t(worker_thread_func);
        std::thread::id worker_thread_id=t.get_id();
        std::cout<<worker_thread_id<<std::endl;
        //获取主线程的id
        std::thread::id main_thread_id = std::this_thread::get_id();
        std::cout<<main_thread_id<<std::endl;
        while (true)
        {
        }
    }
    

    运行结果(ubuntu20):

    image-20210704182253896

3.2.3 等待线程结束

  • 在Linux下等待线程结束

    Linux 线程库提供了pthread_join函数,用来等待某线程的退出并接收他的返回值

    这种操作被称为汇接(join)

    Declared in: pthread.h  
    static int pthread_join(pthread_t __th, void * *__thread_return)
    

    参数__th是需要等待的线程ID;参数__thread__return是输出参数,用于接受被等待线程的退出码,

    可以再调用pthread_exit()时指定退出码

    Declared in: pthread.h  
    static void pthread_exit(void *__retval)
    

    其中__retval可以通过__thread_return参数获得

    下面来展示一个实例,在程序启动时开启一个工作线程,工作线程将当前系统时间写入一个文件后,主线程等待工作线程退出后,从文件中读取时间,并将其输出

    #include <cstdio>
    #include <string>
    #include <pthread.h>
    #include <cstring>
    #define TIME_FILENAME "time.txt"
    void* fileThreadFunc(void* arg)
    {
        time_t now = time(nullptr);
        tm* t = localtime(&now);
        char timeStr[32]={0};
        snprintf(timeStr,32,"%04d/%02d %02d:%02d:%02d",
                 t->tm_year+1900,
                 t->tm_mon+1,
                 t->tm_mday,
                 t->tm_hour,
                 t->tm_min,
                 t->tm_sec);
        FILE* fp = fopen(TIME_FILENAME,"w");
        if(fp == nullptr)
        {
            printf("打开文件(time.txt)失败\n");
            return nullptr;
        }
        size_t sizeToWrite=strlen(timeStr) +1;
        size_t ret = fwrite(timeStr,1,sizeToWrite,fp);
        if(ret != sizeToWrite)
        {
            printf("写入错误!\n");
        }
        fclose(fp);
        return nullptr;
    }
    
    int main()
    {
        pthread_t fileThreadID;
        int ret = pthread_create(&fileThreadID, nullptr,fileThreadFunc, nullptr);
        if(ret != 0)
        {
            printf("创建线程失败\n");
            return -1;
        }
        int* retval;
        pthread_join(fileThreadID,(void**)&retval);
        FILE* fp = fopen(TIME_FILENAME,"r");
        if(fp == nullptr)
        {
            printf("打开文件失败!\n");
            return -2;
        }
    
        char  buf[32] = {0};
        int sizeRead = fread(buf,1,32,fp);
        if(sizeRead == 0)
        {
            printf("读取文件失败");
            fclose(fp);
            return -3;
        }
    
        printf("Current time is: %s.\n",buf);
    
        fclose(fp);
        return 0;
    
    }
    

    执行结果如下:

    image-20210704185826945

  • Windows 等待线程结束

    在Windows下我们可以使用WaitForSingleObject function (synchapi.h)WaitForMultipleObjects function (synchapi.h)

  • C++11提供的等待线程结束的函数

    #include <cstdio>
    #include <cstring>
    #include <thread>
    #define TIME_FILENAME "time.txt"
    void fileThreadFunc()
    {
        time_t now = time(nullptr);
        tm* t = localtime(&now);
        char timeStr[32]={0};
        snprintf(timeStr,32,"%04d/%02d %02d:%02d:%02d",
                 t->tm_year+1900,
                 t->tm_mon+1,
                 t->tm_mday,
                 t->tm_hour,
                 t->tm_min,
                 t->tm_sec);
        FILE* fp = fopen(TIME_FILENAME,"w");
        if(fp == nullptr)
        {
            printf("打开文件(time.txt)失败\n");
            return ;
        }
        size_t sizeToWrite=strlen(timeStr) +1;
        size_t ret = fwrite(timeStr,1,sizeToWrite,fp);
        if(ret != sizeToWrite)
        {
            printf("写入错误!\n");
        }
        fclose(fp);
        return;
    }
    
    int main()
    {
        std::thread t(fileThreadFunc);
        if(t.joinable()) t.join();
    
        FILE* fp = fopen(TIME_FILENAME,"r");
        if(fp == nullptr)
        {
            printf("打开文件失败!\n");
            return -2;
        }
        char  buf[32] = {0};
        int sizeRead = fread(buf,1,32,fp);
        if(sizeRead == 0)
        {
            printf("读取文件失败");
            fclose(fp);
            return -3;
        }
        printf("Current time is: %s.\n",buf);
    
        fclose(fp);
        return 0;
    }
    

  1. CRT原先是指Microsoft开发的C Runtime Library(C语言运行时库),用于操作系统的开发及运行。后来在此基础上开发了C++ Runtime Library,所以现在CRT是指Microsoft开发的C/C++ Runtime Library。在VC的CRT/SRC目录下,可以看到CRT的源码,不仅有C的,也有C++的。CRT(Microsoft’s C/C++ Runtime Library)的一个真子集(主要是C++ Runtime Library)是一个符合(或至少是企图符合)C++标准的C++库。而Windows API(以及Windows的其他许多部分)都是在CRT的基础上开发的。 ??

  2. 既然称作轻量级进程,可见其本质仍然是进程,与普通进程相比,LWP与其它进程共享所有(或大部分)逻辑地址空间和系统资源,一个进程可以创建多个LWP,这样它们共享大部分资源;LWP有它自己的进程标识符,并和其他进程有着父子关系;这是和类Unix操作系统的系统调用vfork()生成的进程一样的。LWP由内核管理并像普通进程一样被调度。Linux内核是支持LWP的典型例子。Linux内核在 2.0.x版本就已经实现了轻量进程,应用程序可以通过一个统一的clone()系统调用接口,*用不同的参数指定创建轻量进程还是普通进程,通过参数决定子进程和父进程共享的资源种类和数量,这样就有了轻重之分*。在内核中, clone()调用经过参数传递和解释后会调用do_fork(),这个核内函数同时也是fork()、vfork()系统调用的最终实现。*在大多数系统中,LWP与普通进程的区别也在于它只有一个最小的执行上下文和调度程序所需的统计信息,而这也是它之所以被称为轻量级的原因。* 因为LWP之间共享它们的大部分资源,所以它在某些应用程序就不适用了;这个时候就要使用多个普通的进程了。例如,为了避免内存泄漏(a process can be replaced by another one)和实现特权分隔(processes can run under other credentials and have other permissions)。img ??

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-04 19:21:28  更:2021-07-04 19:21:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/4 12:18:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码