首页 > 编程语言 >C++创建线程

C++创建线程

时间:2023-04-11 17:45:34浏览次数:53  
标签:mtx int 创建 sum C++ 线程 lock include

C++11 中使用 std::thread 来创建线程。

一、创建线程

#include <iostream>
#include <thread>
#include <mutex>

/* 函数指针创建线程 */
void thread_func(int size) {
    std::cout << "thread_func:" << std::this_thread::get_id() << std::endl;
    
    for (size_t i = 0; i < size; ++i) {
        std::cout << "display form thread:" << std::endl;
    }
}

/* 仿函数创建线程 */
class DisplayThread {
public:
    void operator()() {
        std::cout << "DisplayThread:" << std::this_thread::get_id() << std::endl;
        for (size_t i = 0; i < 10; ++i) {
            std::cout << "display form display:" << std::endl;
        }
    }
};

int main() {
    /* 获取线程ID */
    std::cout << "main:" << std::this_thread::get_id() << std::endl;
    std::thread thread_1(thread_func, 10);
    for (size_t i = 0; i < 10; ++i) {
        std::cout << "display form main_1:" << std::endl;
    }
    /* 阻塞等待线程结束 */
    thread_1.join();

    std::thread thread_2((DisplayThread()));
    /* 线程后台执行,非阻塞 */
    thread_2.detach();


    /* Lambda函数创建线程 */
    std::thread thread_3(
        [] {
            std::cout << "thread_3:" << std::this_thread::get_id() << std::endl;
            for (size_t i = 0; i < 5; ++i) {
                std::cout << "display form thread_3:" << std::endl;
            }
        }
    );

    for (size_t i = 0; i < 5; ++i) {
        std::cout << "display form main_2:" << std::endl;
    }
    thread_3.join();

    system("pause");

    return 0;
}

二、互斥锁的使用

#include <iostream>
#include <thread>
#include <mutex>

void thread_work_1(int& sum) {
    for (size_t i = 1; i < 5000; ++i) {
        sum += i;
    }
}

void thread_work_2(int& sum) {
    for (size_t i = 5000; i <= 10000; ++i) {
        sum += i;
    }
}

int main_func() {
    int sum = 0;
    for (size_t i = 1; i <= 10000; ++i) {
        sum += i;
    }

    return sum;
}

int main() {
    
    int sum = 0;
    std::thread t1(thread_work_1, std::ref(sum));
    std::thread t2(thread_work_2, std::ref(sum));
    t1.join();
    t2.join();
    std::cout << "thread sum:" << sum << std::endl;
    std::cout << "main func sum:" << main_func() << std::endl;

    system("pause");

    return 0;
}

上述代码是求1-10000的和,我们为了提高效率,创建了两个线程同时去计算[1,5000)的和以及[5000,10001)的和,那么用于计算和的变量都用相同的ans来获取结果。为了区别多线程的计算结果,在主线程中调用main_func函数求的结果与其作比较。

我们发现两次的运算结果并不相同,那么我们可以分析一下原因,因为在计算过程中的sum是一个引用,是他们的共享资源,所以当一个线程正在计算+i的时候,此时还没有运算结束,就被切到了另一个线程中,然后在这个线程中可能会计算了很多次+i的操作,然后再切回那个线程中时,计算结果可能就会覆盖掉另一个线程的计算结果,因此这样求出来的数一定是比正确结果要小的,所以为了避免这种情况的发生,引入了互斥锁。互斥锁的重点在于他是一个锁,简单来说就是我们用锁将两个线程中计算过程分别用mutex锁上,那么当一个线程正在计算的时候,另一个线程就会等待这个计算的完成。大致流程是这样的,当work1准备计算sum+=i的时候,用mutex将线程其锁上,如果此时sum+=i还没有计算完就切到了work2的线程时,就会通过mutex检测到已经被锁上了,那么work2就会在此等待work1的计算完成,当work1的计算完成以后就会把锁解开,然后进行下一步的计算。所以两个线程种的计算过程都是加锁-计算-解锁的过程,这样就不会出现上述所说的那种情况了。

#include <iostream>
#include <thread>
#include <mutex>

void thread_work_1(int& sum, std::mutex& mtx_lock) {
    for (size_t i = 1; i < 5000; ++i) {
        mtx_lock.lock();
        sum += i;
        mtx_lock.unlock();
    }
}

void thread_work_2(int& sum, std::mutex& mtx_lock) {
    for (size_t i = 5000; i <= 10000; ++i) {
        mtx_lock.lock();
        sum += i;
        mtx_lock.unlock();
    }
}

int main_func() {
    int sum = 0;
    for (size_t i = 1; i <= 10000; ++i) {
        sum += i;
    }

    return sum;
}

int main() {
    std::mutex mtx_lock;
    int sum = 0;
    std::thread t1(thread_work_1, std::ref(sum), std::ref(mtx_lock));
    std::thread t2(thread_work_2, std::ref(sum), std::ref(mtx_lock));
    t1.join();
    t2.join();
    std::cout << "thread sum:" << sum << std::endl;
    std::cout << "main func sum:" << main_func() << std::endl;

    system("pause");

    return 0;
}

还有一种是用lock_guard类模板,它的内部结构很简单,只有构造函数和析构函数,所以也很容里理解它的工作原理,在实例化对象时通过构造函数实现了lock,在析构函数中实现了unlock的操作。这样就可以避免忘记unlock的情况,具体的实现看下面的代码:

#include <iostream>
#include <thread>
#include <mutex>

void thread_work_1(int& sum, std::mutex& mtx_lock) {
    for (size_t i = 1; i < 5000; ++i) {
        std::lock_guard<std::mutex> mtx_guard(mtx_lock);
        sum += i;
    }
}

void thread_work_2(int& sum, std::mutex& mtx_lock) {
    for (size_t i = 5000; i <= 10000; ++i) {
        std::lock_guard<std::mutex> mtx_guard(mtx_lock);
        sum += i;
    }
}

int main_func() {
    int sum = 0;
    for (size_t i = 1; i <= 10000; ++i) {
        sum += i;
    }

    return sum;
}

int main() {
    std::mutex mtx_lock;
    int sum = 0;
    std::thread t1(thread_work_1, std::ref(sum), std::ref(mtx_lock));
    std::thread t2(thread_work_2, std::ref(sum), std::ref(mtx_lock));
    t1.join();
    t2.join();
    std::cout << "thread sum:" << sum << std::endl;
    std::cout << "main func sum:" << main_func() << std::endl;

    system("pause");

    return 0;
}

 

标签:mtx,int,创建,sum,C++,线程,lock,include
From: https://www.cnblogs.com/QingYiShouJiuRen/p/17307057.html

相关文章

  • C++第二章课后练习题 2-24,2-25
    编写一个完整的程序,实现功能:向用户提问“现在正在下雨吗?”,提示用户输入Y或N。若输入为Y,显示“现在正在下雨。”;若输入为N,显示“现在没有下雨。”;否则继续提问“现在正在下雨吗?”。#include<iostream>usingnamespacestd;intmain(){cout<<"现在正在下雨吗?"<<endl;......
  • 操作系统(2.8.1)--线程的实现方式
    1.内核支持线程(KST)内核支持线程,与进程相同,是在内核的支持下运行的,即无论是用户进程中的线程,还是系统进程中的线程,他们的创建、撤消和切换等也是依靠内核,在内核空间实现的。这种线程实现方式主要有如下四个优点:(1)在多处理器系统中,内核能够同时调度同一进程中多个线程并行执行(2)如......
  • Java创建文件时同时需要创建外层多个文件夹
    在Java中,如果您使用File类创建一个新文件,并且指定的路径中包含不存在的文件夹,那么会抛出IOException异常,因为Java不会自动创建缺少的目录结构。要解决这个问题,可以通过以下方法来手动创建缺失的目录:使用File.mkdirs()方法在所需的目录结构下创建文件夹。例如:1Filefile......
  • pytdx多线程示例
    #encoding=utf-8importmathfrompytdx.hqimportTdxHq_APIimportpathlibimportmultiprocessingasmpfrommultiprocessingimportPoolclassmyTdx:def__init__(self):self.HqHOSTS=pathlib.Path("HqHOSTS.txt").read_text().split(......
  • 标 题: 让 Python 拥有 C/C++ 一样的速度,编译神器 Codon 发布!
    发信人:mseer(mseer),信区:Python标题:让Python拥有C/C++一样的速度,编译神器Codon发布!发信站:水木社区(TueMar1423:52:022023),站内https://mp.weixin.qq.com/s/n5LRxftQiiP8FO6nvyL3-g为了解决这一难题,麻省理工学院的计算机科学家出手了,他们共同研发了一......
  • C++核心编程之-函数提高
    函数提高函数默认参数在c++中,函数的形参列表中的形参是可以有默认值的。语法:返回值类型函数名(参数=默认值){}注意点:1、如果某个位置参数有默认值,那么从这个位置往后,必须都要有默认值​ 2、如果函数声明有默认值,函数实现的时候就不能有默认参数函数占位参数C++中......
  • Solon2 的通讯服务线程配置
    Solon框架,关于通讯服务的所有配置#服务端口(默认为8080)server.port:8080#服务主机(ip)server.host:"0.0.0.0"#服务包装端口(默认为${server.port})//v1.12.1后支持//一般用docker+服务注册时才可能用到server.wrapPort:8080#服务包装主机(ip)//v1.12.1后支持server.wr......
  • 观察者模式重启线程
    观察者模式重启线程看代码的过程中发现了观察者模式用于重启线程的实例,就顺便研究了一下。观察者模式先引用介绍一下观察者模式:意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。主要解决:一个对象状态改变给其......
  • SpringBoot线程池和Java线程池的实现原理
    使用默认的线程池方式一:通过@Async注解调用publicclassAsyncTest{@Asyncpublicvoidasync(Stringname)throwsInterruptedException{System.out.println("async"+name+""+Thread.currentThread().getName());Thread.sleep(10......
  • 多线程事务的提交解决办法
    多线程处理的时候,如果发生了错误,不会因为加了@Transcational注解而生效,这里需要额外使用SqlSessionTemplate{//插入主表electronicTaxBillMapper.insertBatch(masterList);//更新出库单状态outOrderDetailMapper.updateByOrderCodeList(orderCodeList);//切......