多线程编程与资源同步

在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模式下结果:

    在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类型

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

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

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

    运行结果(ubuntu20):

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;}
    

    执行结果如下:

  • 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;
    }
    

3.4.3 C++11 对整型变量原子操作的支持

C++11 提供了对整形变量原子操作的支持

std::atomic这是一个模板类型

Defined in header <atomic>
template< class T >
struct atomic;

例子

#include <atomic>
#include <iostream>
int main()
{std::atomic<int> value{1};value++;//自增,原子操作std::cout<<value<<std::endl;
}

C++ 线程同步对象

C++ 新标准中新增用于线程同步的std::mutexstd::condition_variable

3.7.1 std::mutex系列

这个系列类型的对象均提供了加锁lock,尝试加锁trylock和解锁unlock的方法

#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
int  g_num =0 ;
std::mutex g_num_mutex;
void slow_increment(int id)
{for (int i = 0; i < 10; ++i){g_num_mutex.lock();++g_num;std::cout<<id<<"->"<<g_num<<std::endl;g_num_mutex.unlock();}std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{std::thread t1(slow_increment,0);std::thread t2(slow_increment,1);t1.join();t2.join();return 0;
}

输出:

3.7.2 std::shared_mutex

std::shared_mutex的底层实现是操作系统提供的读写锁,也就是说,在有多个线程对共享资源读且少许线程对共享资源写的情况下,std::shared_mutexstd::mutex效率更高

shared_mutex 通常用于多个读线程能同时访问同一资源而不导致数据竞争,但只有一个写线程能访问的情形。

3.7.3 std::condition_variable

Member functions

(constructor) constructs the object (public member function)
(destructor) destructs the object (public member function)
operator=[deleted] not copy-assignable (public member function)
Notification
notify_one notifies one waiting thread (public member function)
notify_all notifies all waiting threads (public member function)
Waiting
wait blocks the current thread until the condition variable is woken up (public member function)
wait_for blocks the current thread until the condition variable is woken up or after the specified timeout duration (public member function)
wait_until blocks the current thread until the condition variable is woken up or until specified time point has been reached (public member function)
Native handle
native_handle returns the native handle (public member function)

`

TCP网络编程的基本流程

Linux与C++11多线程编程(学习笔记)

Linux select函数用法和原理

socket的阻塞模式和非阻塞模式(send和recv函数在阻塞和非阻塞模式下的表现)

connect函数在阻塞和非阻塞模式下的行为

获取socket对应的接收缓冲区中的可读数据量


  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)。 ↩︎

Linux与C++11多线程编程(学习笔记)相关推荐

  1. 多线程编程学习笔记——线程池(二)

    接上文 多线程编程学习笔记--线程池(一) 三.线程池与并行度 此示例是学习如何应用线程池实现大量的操作,及与创建大量线程进行工作的区别. 1. 代码如下 using System; using Sy ...

  2. 多线程编程学习笔记——async和await(三)

    接上文 多线程编程学习笔记--async和await(一) 接上文 多线程编程学习笔记--async和await(二) 五.   处理异步操作中的异常 本示例学习如何在异步函数中处理异常,学习如何对多 ...

  3. 多线程编程学习笔记——任务并行库(二)

    接上文 多线程编程学习笔记--任务并行库(一) 三.   组合任务 本示例是学习如何设置相互依赖的任务.我们学习如何创建一个任务的子任务,这个子任务必须在父任务执行结束之后,再执行. 1,示例代码如下 ...

  4. 多线程编程学习笔记——任务并行库(三)

    接上文 多线程编程学习笔记--任务并行库(一) 接上文 多线程编程学习笔记--任务并行库(二) 六.   实现取消选项 本示例学习如何实现基于Task的异步操作进行取消流程,以及在任务真正运行前如何知 ...

  5. 多线程编程学习笔记——使用并发集合(三)

    接上文 多线程编程学习笔记--使用并发集合(一) 接上文 多线程编程学习笔记--使用并发集合(二) 四.   使用ConcurrentBag创建一个可扩展的爬虫 本示例在多个独立的即可生产任务又可消费 ...

  6. 【多线程编程学习笔记6】终止线程执行,千万别踩这个坑!

    申明:本学习笔记是在该教程的基础上结合自己的学习情况进行的总结,不是原创,想要看原版的请看C语言中文网的多线程编程(C语言+Linux),该网站有很多好的编程学习教程,尤其是关于C语言的. 在< ...

  7. python3多线程编程_Python 3多线程编程学习笔记-基础篇

    本文是学习<Python核心编程>的学习笔记,介绍了Python中的全局解释器锁和常用的两个线程模块:thread, threading,并对比他们的优缺点和给出简单的列子. 全局解释器锁 ...

  8. DirectX 11游戏编程学习笔记之1: 开场白

    本文由哈利_蜘蛛侠原创,转载请注明出处!有问题欢迎联系2024958085@qq.com 这是我之前的博客系列"DirectX9.0c游戏开发手记之'龙书'第二版学习笔记"的平行版 ...

  9. 多线程编程学习笔记1时间

    时间 c语言如何处理时间 c语言如何处理时间:time.h long t0 = time(NULL) ;//获取unix时间(从1970年1月1日到当前时经过的秒数 sleep(3)://让程序休眠3 ...

最新文章

  1. golang 映射 map 简介
  2. scala(10)-----Scala 闭包
  3. 修改 连接层_Mybatis连接池_动态sql语句_多表查询实现
  4. 转:PHP中文乱码问题
  5. Ubuntu X64 系统安装配置编录
  6. java音乐登陆界面_onlinemusic java在线音乐平台,实现了基本的注册登录功能,有界面,对 的初学者有帮助 Jsp/Servlet 238万源代码下载- www.pudn.com...
  7. 线性回归中一次性实现所有自变量的单因素分析
  8. 背单词的小智 (二分)
  9. 将文件目录生成文档目录或者excel目录
  10. android实现延时的方法,Android实现延时总结
  11. meta camp+21春季PAT乙级反思
  12. ae制h5文字动画_对于8个华丽的HTML5文字动画特效图文赏析
  13. vue+Element做表格的批量增加
  14. 微信公众号文章爬取下载各种格式
  15. ol-ext transform 对象,旋转、拉伸、放大(等比例缩放),事件监听
  16. 日记侠:朋友圈一定要刷屏吗?
  17. 促卵泡激素(FSH)研究丨重组人卵泡刺激素
  18. 检测labview安装visa驱动,并实现串口仪器控制(上篇)
  19. Returnil Virtual System Personal/Business Beta 1.70.6160
  20. 【Leetcode】416 分割等和子集

热门文章

  1. 【51单片机快速入门指南】4.4.1:python串口接收磁力计数据并进行最小二乘法椭球拟合
  2. 机器学习实战 | SKLearn最全应用指南
  3. Linux 下wifi 驱动开发(二)—— WiFi模块浅析
  4. Exynos4412 IIC总线驱动开发(二)—— IIC 驱动开发
  5. 【Linux】FTP文件下载
  6. 简单调试 Python 程序
  7. Linux下检测网络状态是否正常
  8. vue如何引入ant部分组件
  9. ant中的loading按钮使用
  10. 安卓App报错:android.os.FileUriExposedException