首页 > 其他分享 >两个线程交替打印一个共享变量

两个线程交替打印一个共享变量

时间:2023-03-01 16:35:32浏览次数:41  
标签:mtx int lock 打印 flag 线程 共享 include

首先给出基本框架

#include <iostream>
#include <thread>

using namespace std;

int main(){
    int n = 100;
    int i = 0;
    //创建两个线程
    thread newThread1([&n, &i](){
        while(i < n){
            cout << this_thread::get_id() << ": " << i << endl;
            i++;
        }    
    });
    thread newThread2([&n, &i](){
        while(i < n){
            cout << this_thread::get_id() << ": "  << i << endl;
            i++;
        }    
    });
    
    if(newThread1.joinable()){
        newThread1.join();
    }
    if(newThread2.joinable()){
        newThread2.join();
    }
    
    return 0;
}


这显然没有完成两个线程交替打印的目的,因为共享变量i是临界资源,所以会造成线程争抢,线程不安全,解决方法也很简单,创建一个互斥量mutex并在临界区合适的地方加锁和解锁。由于线程执行函数使用了lambda表达式,为了让两个线程使用同一把锁,把锁创建在了main函数内,并在lambda表达式内使用了引用捕捉。

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

int main(){
    int n = 100;
    int i = 0;
    mutex mtx;
    //创建两个线程
    thread newThread1([&n, &i, &mtx](){
        while(i < n){
            mtx.lock();
            cout << this_thread::get_id() << ": " << i << endl;
            i++;
            mtx.unlock();
        }    
    });
    thread newThread2([&n, &i, &mtx](){
        while(i < n){
            mtx.lock();
            cout << this_thread::get_id() << ": "  << i << endl;
            i++;
            mtx.unlock();
        }    
    });
    
    if(newThread1.joinable()){
        newThread1.join();
    }
    if(newThread2.joinable()){
        newThread2.join();
    }
    
    return 0;
}

但是在C++中,一般不操作锁,而是由类去管理,管理锁的类一般分为:

  • template <class Mutex> class lock_guard;
  • template <class Mutex> class unique_lock;
    其中第一种只有构造和析构函数,是锁的RAII简单实现。
    第二种是通用互斥锁包装器,支持延迟锁定,锁定的有时限尝试,递归锁定,所有权转移和与条件变量一起使用
    unique_lock比lock_guard更加灵活,功能更加强大;带来的缺点就是付出更多的时间和性能成本。
    这里使用第二种以便于之后与条件变量一起使用:
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

int main(){
    int n = 100;
    int i = 0;
    mutex mtx;
    //创建两个线程
    thread newThread1([&n, &i, &mtx](){
        while(i < n){
            unique_lock<mutex> LockManage(mtx);
            cout << this_thread::get_id() << ": " << i << endl;
            i++;
        }    
    });
    thread newThread2([&n, &i, &mtx](){
        while(i < n){
            unique_lock<mutex> LockManage(mtx);
            cout << this_thread::get_id() << ": "  << i << endl;
            i++;
        }    
    });
    
    if(newThread1.joinable()){
        newThread1.join();
    }
    if(newThread2.joinable()){
        newThread2.join();
    }
    
    return 0;
}

此时线程安全,但是若其中一个线程竞争锁的能力比较强,就会出现上述情况,大多数打印操作均由一个线程完成。
添加控制:一个线程执行一次后,再去执行就不被允许了,同时唤醒另外一个线程去执行。解决方案是添加一个条件变量,让某个线程在该条件变量下的阻塞队列等待。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;

int main(){
    int n = 100;
    int i = 0;
    mutex mtx;
    condition_variable cv;
    bool flag = false;
    //创建两个线程
    thread newThread1([&n, &i, &mtx, &cv, &flag](){
        while(i < n){
            unique_lock<mutex> LockManage(mtx);
            //!flag为真,获取后不会阻塞,优先运行
            cv.wait(LockManage, [&flag]() {return !flag; });
            cout << this_thread::get_id() << ": " << i << endl;
            i++;
            flag = true;
            cv.notify_one();
            //调用notify_one()随机唤醒一个阻塞的线程,而其余线程仍处于阻塞状态,等待下一次唤醒。
        }    
    });
    thread newThread2([&n, &i, &mtx, &cv, &flag](){
        while(i < n){
            unique_lock<mutex> LockManage(mtx);
            //!flag为假,竞争到锁后由于条件不满足,阻塞
            cv.wait(LockManage, [&flag]() {return flag; });
            cout << this_thread::get_id() << ": "  << i << endl;
            i++;
            flag = false;
            cv.notify_one();
        }    
    });
    
    if(newThread1.joinable()){
        newThread1.join();
    }
    if(newThread2.joinable()){
        newThread2.join();
    }
    
    return 0;
}

最终实现了两进程交替打印奇偶数。

标签:mtx,int,lock,打印,flag,线程,共享,include
From: https://www.cnblogs.com/forlqy/p/liang-ge-xian-cheng-jiao-ti-da-yin-yi-ge-gong-xian.html

相关文章

  • 多线程和多进程的区别
    一个线程从属于一个进程;一个进程可以包含多个线程。一个线程挂掉,对应的进程挂掉,多线程也挂掉;一个进程挂掉,不影响其它进程,多进程稳定。进程系统开销显著大于线程开销;线程......
  • 软件开发商共享加密锁方案
    多个软件开发商共享加密锁方案通常是指采用一种名为“加密锁共享方案”的技术。这种技术是指多个软件开发商使用同一款加密锁设备,将各自的软件产品与该加密锁设备绑定,从而......
  • IDEA 打印和响应数据中文乱码
     修改Editor-fileEncodeing和console配置,控制台可以正常显示中文,但是接口的响应的数据部分仍然为乱码,idea编译的编码格式有问题,在javacompiler里添加-encodingutf-......
  • 还不知道线程池的好处?快来了解一下
    摘要:线程池的好处:重用存在的线程,减少对象创建、消亡的开销,性能佳;可以有效控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞。本文分享自华为云社区《......
  • 还不知道线程池的好处?快来了解一下
    摘要:线程池的好处:重用存在的线程,减少对象创建、消亡的开销,性能佳;可以有效控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞。本文分享自华为云社区......
  • jmeter跨线程组调用变量-以token为例
    跨线程组调用变量的解决方法:在beanshell取样器中使用setProperty函数设置全局变量,其他线程组用P函数调用全局变量 跨线程组调用变量的步骤:以token为例跨线程组调用有两......
  • 第8章:多线程
    第8章多线程1、基本概念:程序、进程、线程程序(program):是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。进程(process):是程序的一次执......
  • Java——四种线程创建方式
    java中创建线程有四种方式,分别是:继承Thread类,重写run方法,然后创建线程对象并调用start方法。实现Runnable接口,实现run方法,然后创建线程对象并传入Runnable实例,再调用start......
  • vue中实现打印和导出功能
    导出功能项目中需要用到导出,打印功能,实现之后记录下,为了以后可以进一步的使用首先需要安装几个插件npminstall-Sfile-saverxlsxnpminstall-Dscript-loaderorc......
  • C# Winform 多线程更新界面UI控件,解决界面卡顿问题(转)
    前言    多线程刷新界面主要用到多线程,委托,线程安全、事件等一系列高难度的C#操作。1、使用timer控件对要刷新的控件进行定时刷新    对刷新频率要求不......