首页 > 编程语言 >C++ 多线程笔记1 线程的创建

C++ 多线程笔记1 线程的创建

时间:2024-02-28 23:47:39浏览次数:16  
标签:std 多线程 mainThreadId C++ 线程 include id

C++ 多线程笔记1 线程的创建

里面代码会用到的头文件

#include <iostream>
#include <string>
#include <memory>
#include <thread>
#include <vector>
#include <stdlib.h>
#include <cmath>
#include <chrono>
#include <ctime>

入门例子

void mytest()
{
    std::cout<<"hello"<<'\n';
}
int main()
{
    std::thread t(mytest); //开启一个线程
    std::cout<<"hello world\n";
    t.join(); //终结线程
  
}
/*输出
hello world
hello

*/
  • 1.上面的代码使用了std::thread t,这行代码创建了一个新的线程t,并让它执行mytest函数。当这行代码执行时,mytest函数会在新创建的线程上开始执行。主线程(即创建t的线程)则会继续执行下一行代码。

  • 2.t.join()是一个阻塞调用,意味着主线程会等待,直到线程t(即mytest函数)执行完毕。换句话说,t.join()确保了mytest函数在新线程上完成执行后,主线程才会继续执行后续的代码。如果没有t.join() ,主线程可能在mytest函数完成之前结束,这可能会导致未定义的行为,因为当主线程结束时,所有其他线程都会被强制终止,即使它们还没有完成它们的任务。

利用多线程去计算

多线程可以加快数组元素的计算,例如,我们先创建一个数组,来进行普通的计算,顺便测试一下运行速度。

//一个简单计算的例子
double cf(double v)
{
    if (v<=0)
    return v;
    std::this_thread::sleep_for(std::chrono::milliseconds(1));//让线程停下来1毫秒
    return sqrt((v * v+std::sqrt((v-5)*(v+2.5) )/2.0 )/v); //随便的计算
}

std::vector<double> vds;
    for(int i=0;i<1000;++i)
    vds.push_back(rand()); //随机赋值
    std::cout<<vds.size()<<'\n';  //大小
    double value=0.0;
auto nowc = clock();//现在的时间
    for(auto& info :vds)
    {
        value += cf(info);
    }   
    std::cout<<"value: "<<value<<"spend time is "<<clock()-nowc<<'\n';

输出内容

1000
value: 120539spend time is 16760

接下来,我们使用多线程来计算一下数组元素,我们让主线程计算一半数组元素,开一个线程计算另外一半数组元素。
在多线程中

//这个函数是给数组中从Begin位置到End位置中元素进行计算
template<class T,typename Fun>
double visit(std::thread::id id,T iterBegin,T iterEnd,Fun f)
{
   auto mainThreadId = std::this_thread::get_id();//获取主线程id
   if(id == mainThreadId)
    std::cout<<"This is MAIN Thread \n";
    else
    std::cout<<"This is WORK Thread \n";

    double v=0;
    for(auto iter=iterBegin; iter!=iterEnd;++iter)
    v+=f(*iter);
    return v;
}

 auto iter = vds.begin() +(vds.size()/2);//数组的后一半
    double anotherv = 0.0;
    auto iterEnd = vds.end();
    nowc = clock();
    std::thread s( //这里的mainThreadId
        [&anotherv,iter,iterEnd,mainThreadId]()
        {
            anotherv=visit(mainThreadId,iter,iterEnd,cf);//将当前线程id,计算数组后半的数据
        });
    auto halfv = visit(mainThreadId,vds.begin(),iter,cf);  //计算数组前半的数据
    s.join();
    std::cout<<"halfv+anotherv: "<<(halfv+anotherv)<<"spend time is "<<clock()-nowc<<'\n';  

输出内容

This is MAIN Thread
This is WORK Thread
halfv+anotherv: 120539spend time is 7890

由此可见,用多线程的技术后,计算速度比普通计算快接近一半.我们还可以建立三线程等更多线程来计算

//三线程代码
auto mainThreadId = std::this_thread::get_id();//获取主线程id

    std::vector<double> vds;
    for(int i=0;i<300;++i)
    vds.push_back(rand());
    std::cout<<vds.size()<<'\n';
    double value=0.0;
    
    // 让我们测试一下运行速度
    auto nowc = clock();//现在的时间
    for(auto& info :vds)
    {
        value += cf(info);
    }   
    std::cout<<"value: "<<value<<" spend time is "<<clock()-nowc<<'\n';

    auto begin1 = vds.begin();
    auto iter = vds.begin() +(vds.size()/3);
    auto iter2 = iter+(vds.size()/3);

    double anotherv = 0.0;
    double anotherv2 = 0.0;
    auto iterEnd = vds.end();
    nowc = clock();
    std::thread s( //这里的mainThreadId
        [&anotherv,begin1,iter,mainThreadId]()
        {
            anotherv=visit(mainThreadId,begin1,iter,cf);//将当前线程id,计算数组后半的数据
        }); 
     std::thread s2( //这里的mainThreadId
        [&anotherv2,iter,iter2,mainThreadId]()
        {
            anotherv2=visit(mainThreadId,iter,iter2,cf);//将当前线程id,计算数组后半的数据
        });
            
    auto halfv = visit(mainThreadId,iter2,vds.end(),cf);  //计算数组前半的数据
    s.join();
    s2.join();
    std::cout<<"halfv+anotherv: "<<(halfv+anotherv+anotherv2)<<" spend time is "<<clock()-nowc<<'\n';  
/*
输出
300
value: 36321.9 spend time is 4734
This is MAIN Thread
This is WORK Thread
This is WORK Thread
halfv+anotherv: 36321.9 spend time is 1582
*/

关于线程中的 mainThreadId 还有几点,有以下几点需要澄清:

  • 线程 ID 的唯一性:1std::this_thread::get_id() 1返回调用线程的唯一标识符。因此,主线程和任何新创建的线程都会有不同的 ID。即使您在主线程中和新线程中都调用了 std::this_thread::get_id() 并将其赋值给 mainThreadId,这两个变量也会持有不同的值,因为它们是不同线程的 ID。

  • 捕获列表的作用:在 s 线程的捕获列表中,1mainThreadId 是通过引用捕获的1。这意味着 s 线程中的 mainThreadId 是 main 函数中 mainThreadId 的一个引用。但是,这并不意味着 s 线程中的 mainThreadId 就是主线程的 ID。它只是 main 函数中定义的同一个变量的引用。

  • 变量的生命周期:只要 main 函数还在执行,并且 mainThreadId 还在其作用域内,s 线程就可以通过其捕获的引用访问 mainThreadId。但是,一旦 main 函数结束,mainThreadId 的生命周期也就结束了,此时对 s 线程中捕获的引用的访问将是未定义的。

标签:std,多线程,mainThreadId,C++,线程,include,id
From: https://www.cnblogs.com/AndreaDO/p/18041511

相关文章

  • c++ queue在多线程中的使用
    queue队列,先进先出。多线程的一种使用案例:生产者每3spush一个元素消费者每5s才能pop一个元素(队首)那么,2个消费者就可以及时地消耗掉push的元素。#include<iostream>#include<thread>#include<mutex>#include<queue>std::queue<int>m_queue;std::mutexm_mutex;std:......
  • [没啥用科技] C++ 分数类
    虽然说用的是结构体,但已经实现了同类型加减乘除和分数与整型的加减乘除。写的有点难看,并伴有大常数,以后来改。#include<bits/stdc++.h>#include<bits/extc++.h>#definelllonglong#defineldblongdouble#definem_p(a,b)make_pair(a,b)usingnamespacestd;using......
  • c++ primer ch2笔记
    ch22.1基本内置类型C++基本内置类型void算术类型整形(包括字符,bool)浮点型最小尺寸:整形尺寸大小受编译器影响,但是至少会保证一个最小尺寸,int最小尺寸2字节相互关系:int至少和一个short一样大无符号类型:unsignedint、unsignedlong类型转换规则:布......
  • rust与python/c++语法区别
    if/matchpubfnanimal_habitat(animal:&str)->&'staticstr{letid=ifanimal=="crab"{//id等于(或拥有)了一个匿名函数的返回值1}elseifanimal=="gopher"{2}elseifanimal=="snake"......
  • 线程
    ##1.iOS中有多少类型的线程?*PThread:跨系统,对C语言的封装。很少使用,不推荐使用。*NSThread:面向对象,需要手动管理生命周期。*GCD:GrandCentralDispatch,主打任务与队列。*NSOperation&NSOperationQueue:基于GCD的封装,面向对象。##2.GCD有那些队列,默认提供那些队......
  • C++的异常处理究竟有多慢?
    我们能在各处看到“C++的异常很慢,不要用”这样的言论,不过很少有人告诉你,C++的异常到底有多慢,以及它为什么这么慢。本文会详细介绍C++在抛出以及捕获异常时,代码到底做了什么,以及我们使用C++异常到底付出了多少代价。抛出异常要了解异常处理到底做了什么,我们需要深入到汇编语言来......
  • 通过前向声明解决C++中两个头文件互相引用的问题
    在C++中,当两个头文件互相引用时,可以通过前向声明来避免直接的#include依赖,从而解决循环依赖的问题。前向声明是在一个头文件中声明另一个头文件中的类或类型的名称,而不包括其具体的实现细节。这样,每个头文件只依赖对方的声明,而不需要依赖对方的定义,从而打破了循环依赖。以下是如......
  • python中5种线程锁
    线程安全线程安全是多线程或多进程编程中的一个概念,在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。线程安全的问题最主要还是由线程切换导致的,比如一个房间(进程)中有10颗糖(资源),除此之外......
  • OpenCV计数应用 c++(QT)
    一、前言为了挑战一下OpenCV的学习成果,最经一直在找各类项目进行实践。机缘巧合之下,得到了以下的需求:要求从以下图片中找出所有的近似矩形的点并计数,重叠点需要拆分单独计数。二、解题思路1.图片作二值化处理autoimage=cv::imread("points.jpg");cv::Matborder;//为......
  • C++临时对象
    C++临时对象临时对象的构造与析构在C++中,临时对象(TemporaryObject)是在表达式求值过程中创建的、无名字的对象。它们通常用于存储中间结果或作为函数调用的参数或返回值,其生命周期通常仅限于表达式的求值过程中。临时对象的构建和析构与普通对象类似,只是它们的生命周期通常比......