c++标准库 并发编程 相关示例

本文介绍了C++11标准库中的线程启动接口,如std::async、std::packaged_task和std::thread,并讨论了线程不安全的情况,如未同步数据访问、写至一半的数据和重新安排的语句。为了解决这些问题,文章提到了原子性、次序等性质,并展示了Mutex、Lock、condition_variable和atomic等工具的使用示例。此外,通过多个示例代码演示了如何在实践中确保线程安全,包括多线程同步、数据共享和异常处理等场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

c++11 标准库,启动线程的接口有如下:

1. std::async();

2. std::packaged_task

3. std::thread

线程不安全, 在什么情况发生:

1. Unsynchronized data access(未同步化的数据访问):并行运行的两个线程读和写同一笔数据,不知道哪一个语句先来。

2.Half-written data(写至一半的数据):某个线程正在读数据, 另一个线程改动它, 于是读取中的线程甚至可能读到改了一半的数据,读到一个半新半旧值。

3.Reordered statement(重新安排的语句):语句和操作有可能被重新安排次序(reordered),也许对于每一个单线程正确, 但对于多个线程的组合却破坏了预期的行为。 

解决线程同步安全问题所需要的性质:

1. Atomicity(不可切割性):这意味着读或写一个变量, 其行为是独占的,排他的, 无任何打断,因此一个线程不可能读到“因另一线程而造成的”中间状态。

2.Order(次序): 我们需要一些方法保证“具体指定之语句”的次序。

标准库解决线程同步化与并发所提供的工具有:

1. Mutex  和 Lock: 包括符合RAII 特性的 std::lock_guard; 递归的 std::recursive_mutex ;等等,具体看下面代码示例。

2.std::condition_variable (条件变量):适合用于不同线程执行的task必须彼此等待的情形, 具体看下面代码示例。

3.std::atomic: 保证提供了顺序一致性。 示例如下。

// 多线程TEST.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <future>
#include <thread>
#include <chrono>
#include <random>
#include <iostream>
#include <exception>
#include <Windows.h>
#include <condition_variable>
#include <queue>
using namespace std;

void doSomething(char c) {
    default_random_engine dre(c);
    uniform_int_distribution<int> id(10,1000);
    for (int i = 0; i < 10; i++) {
        this_thread::sleep_for(chrono::milliseconds(id(dre)));
        cout.put(c).flush();
    }
}
class X {
public:
    void mem(int num) {
        cout << num << endl;
    }
};
void test1() {
    std::cout << "Starting 2 opreator asyncchronously !\n";
    //
    //std::future<void> f1 = async([] {doSomething('.'); });
    char c = '@';
    std::future<void> f1 = async(doSomething, std::ref(c));
    std::future<void> f2 = async([] {doSomething('+'); });
    //
    if (f1.wait_for(chrono::seconds(0)) != future_status::deferred ||
        f2.wait_for(chrono::seconds(0)) != future_status::deferred)
    {
        //poll unti at least one of the loops finished
        while (f1.wait_for(chrono::seconds(0)) != future_status::ready &&
            f2.wait_for(chrono::seconds(0)) != future_status::ready) {
            this_thread::yield();
        }
    }
    cout.put('\n').flush();
    X x;
    auto a = std::async(&X::mem, x, 23);
    try {
        f1.get();
        //  f1.get();只能调用一次get,否则会异常
        f2.get();
    }
    catch (const exception& e) {
        cout << "\n EXCREPTION:" << e.what() << endl;
    }
    cout << "\n done" << endl;
}
/// <summary>
/// /////////////////////
/// </summary>
/// <returns></returns>
int queryNumber() {
    cout << "read number:";
    int num;
    cin >> num;
    if (!cin) {
        throw runtime_error("no number read");
    }
    return num;
}

void doSomething2(char c, shared_future<int> f) {
    try {
        int num = f.get();
        for (int i = 0; i < num; i++) {
            this_thread::sleep_for(chrono::milliseconds(1000));
            cout.put(c).flush();
        }
    }
    catch (const exception& e) {
        cerr << "Exception in thread" << this_thread::get_id() << ":" << e.what() << endl;
    }
}

void doSomething3(char c, shared_future<int>& f) {
    try {
        int num = f.get();
        for (int i = 0; i < num; i++) {
            this_thread::sleep_for(chrono::milliseconds(1000));
            cout.put(c).flush();
        }
    }
    catch (const exception& e) {
        cerr << "Exception in thread" << this_thread::get_id() << ":" << e.what() << endl;
    }
}

void test2() {
    try
    {
        shared_future<int> f = async(queryNumber);
        future<void> f1 = async(launch::async, doSomething2, '.', f);
        future<void> f2 = async(launch::async, doSomething2, '+', f);
        future<void> f3 = async(launch::async, doSomething3, '@', std::ref(f));
        
        f1.get();
        f2.get();
        f3.get();
    }
    catch (const std::exception& e)
    {
        cout << "\n EXCEPTION :" << e.what() << endl;
    }
    cout << "\n done" << endl;

}
/// <summary>
/// 
/// </summary>
/// <param name="num"></param>
/// <param name="c"></param>
void doSomething4(int num, char c) {
    try {
        default_random_engine dre(42*c);
        uniform_int_distribution<int> id(10,1000);
        for (int i = 0; i < num; ++i) {
            this_thread::sleep_for(chrono::milliseconds(id(dre)));
            cout.put(c).flush();
        }
    }
    catch (const exception& e) {
        cerr << "Exception in thread" << this_thread::get_id() << ":" << e.what() << endl;
    }
    catch (...) {
        cerr << "Exception in thread" << this_thread::get_id();
    }
}
void test3() {
    try
    {
        thread t1(doSomething4, 10, '.');
        cout << "-started fg thread:" << t1.get_id() << endl;
        
        for (size_t i = 0; i < 5; i++)
        {
            thread t(doSomething4, 5, 'a' + i);
            cout << "-deatch started bg thread" << t.get_id() << endl;
            t.detach();//很少使用, 危险, 退出程序, 线程仍然可能还在运行。
        }
        cin.get();
        cout << "-join fg thread" << t1.get_id() << endl;
        t1.join();
        cout << "-exit"  << endl;
    }
    catch (const std::exception& e)
    {
        cerr << "exception :" << e.what() << endl;
    }
}
/// <summary>
/// 
/// </summary>
/// <returns></returns>
/// 
void doSomething5(std::promise<std::string>& p ) {
    try {
        cout << "read char('x' for exception):";
        char c = std::cin.get();
        if (c == 'x') {
            throw std::runtime_error(std::string("char ")+c+" read");
        }
        std::string s = std::string("char ") + c + " processed";
        p.set_value(std::move(s));
    }
    catch (...) {
        //cerr << "Exception in thread" << this_thread::get_id();
        p.set_exception(std::current_exception());
    }
}

void test4() {
    try
    {
        std::promise<string> p;
        std::thread t(doSomething5, std::ref(p));
        t.detach();
        //
        std::future<std::string> f(p.get_future());
        //
        std::cout << "result " << f.get() << std::endl;
    }
    catch (const std::exception& e)
    {
        std::cerr << "EXCEPTION: " << e.what() << std::endl;
    }
    catch (...) {
        std::cerr << "EXCEPTION: " << std::endl;
    }
}
/// <summary>
/// 
/// </summary>
/// <returns></returns>
double compute(int x, int y) {
    cout << "start thread id:" << this_thread::get_id()<<endl;
    this_thread::sleep_for(chrono::milliseconds(500));
    return (x + y);
}
void test5() {
    std::packaged_task<double(int, int)> task(compute);
    std::future<double> f = task.get_future();
    //
    cin.get();
    cout << "pepare start"<<endl;
    task(5, 6);
    double res = f.get();
    cout << "res compute(5,6)=" << res << endl;
}

//
std::mutex printMutex;
void print(const std::string& s) {
    std::lock_guard<std::mutex> l(printMutex);
    for (char c:s)
    {
        std::cout.put(c);
    }
    std::cout << std::endl;
}
void test6() {
    auto f1 = std::async(std::launch::async, print, "hello from a first thread");
    auto f2 = std::async(std::launch::async, print, "hello from a second thread");
    print("hell from the main thread");
}
//递归锁
class DatabaseAccess {
private :
    std::recursive_mutex dbMutex;
public:
    void createTable() {
        std::lock_guard<std::recursive_mutex> lg(dbMutex);
        std::cout << "create table" << endl;
    }
    void insertData() {
        std::lock_guard<std::recursive_mutex> lg(dbMutex);
        std::cout << "insert data table" << endl;
    }
    void createTableAndInsertData() {
        std::lock_guard<std::recursive_mutex> lg(dbMutex);
        createTable();
        insertData();
        std::cout << "createTableAndInsertData" << endl;
    }
};

void test7() {
    DatabaseAccess db;
    db.createTableAndInsertData();
}
//
//
class DatabaseAccess1 {
private:
   // std::timed_mutex dbMutex;
    std::recursive_timed_mutex dbMutex;
public:
    void createTable() {
      //  std::lock_guard<std::recursive_mutex> lg(dbMutex);
        if (dbMutex.try_lock_for(chrono::milliseconds(500))) {
            std::lock_guard<std::recursive_timed_mutex> lg(dbMutex, std::adopt_lock);
            std::cout << "create table" << endl;
        }
        else {
            std::cout << "try lock failed createTable" << endl;
        }
       
    }
    void insertData() {
        if (dbMutex.try_lock_for(chrono::milliseconds(500))) {
            std::lock_guard<std::recursive_timed_mutex> lg(dbMutex, std::adopt_lock);
            std::cout << "insert data table" << endl;
        }
        else {
            std::cout << "try lock failed insertData" << endl;
        }
       
    }
    void createTableAndInsertData() {
        if (dbMutex.try_lock_for(chrono::milliseconds(500))) {
            std::lock_guard<std::recursive_timed_mutex> lg(dbMutex, std::adopt_lock);
            createTable();
            insertData();
            std::cout << "createTableAndInsertData" << endl;
        }
        else {
            std::cout << "try lock failed createTableAndInsertData" << endl;
        }
    }
};
void test8() {
    DatabaseAccess1 db;
    db.createTableAndInsertData();
}
//
void test9() {
    std::mutex m1;
    std::mutex m2;
    int idx = std::try_lock(m1, m2);
    if (idx < 0) {
        std::lock_guard<mutex> lg1(m1, std::adopt_lock);
        std::lock_guard<mutex> lg2(m2, std::adopt_lock);
        cout << "lock success" << endl;
    }
    else {
        std::cerr << "coult not lock " << endl;
    }
}
//
void initialized1() {}
void test10() {
    bool initialized = false;
    std::once_flag oc;
    std::call_once(oc, initialized1 );
}
//多线程环境中,缓式初始化
class B {
private:
    mutable std::once_flag initDataFlag;//mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
    void initData() const {} 
public:
    string getData() const {
        std::call_once(initDataFlag, &B::initData, this);
        return "success";
    }

};
//条件变量 condition_variable
namespace conditionTest {
    bool readyFlag;
    std::mutex readyMutex;
    std::condition_variable readyCondVar;
    void thread1() {
        std::cout << "<return>" << std::endl;
        std::cin.get();
        {
            std::lock_guard<std::mutex> lg(readyMutex);
            readyFlag = true;
        }
        readyCondVar.notify_one();
    }
    void thread2() {
        {
            std::unique_lock<std::mutex> ul(readyMutex);
            readyCondVar.wait(ul, [] {return readyFlag; });
        }
        std::cout << "done" << std::endl;
    }

}
void test11() {
    auto f1 = std::async(std::launch::async, conditionTest::thread1);
    auto f2 = std::async(std::launch::async, conditionTest::thread2);
}
//
namespace conditionTest2 {
    std::queue<int> queue;
    std::mutex queueMutex;
    std::condition_variable queueCondVar;
//
    void provider(int val) {
        for (int i=0;i<6;++i)
        {
            std::lock_guard<std::mutex> lg(queueMutex);
            queue.push(val+i);
            std::cout << "provider:" << val + i << endl;
        }
        queueCondVar.notify_one();
        std::this_thread::sleep_for(std::chrono::milliseconds(val));
    }
    //
    void consumer(int num) {
        while (true) {
            int val;
            {
                std::unique_lock<std::mutex> ul(queueMutex);
                queueCondVar.wait(ul, [] {return !queue.empty(); });
                val = queue.front();
                queue.pop();
               /// std::cout << "consumer:" << val<<endl;
            }
            std::cout << "consumer:" << num<<":"<<val << endl;
        }
    } 
}
void test12() {
    auto p1 = std::async(std::launch::async, conditionTest2::provider, 100);
    auto p2 = std::async(std::launch::async, conditionTest2::provider, 300);
    auto p3 = std::async(std::launch::async, conditionTest2::provider, 500);
    //
    auto c1 = std::async(std::launch::async, conditionTest2::consumer, 1);
    auto c2 = std::async(std::launch::async, conditionTest2::consumer, 2);


}
//atomicity
namespace AtomicityTest {
    long data;
    std::atomic<bool> readyFlag(false);
    void provider() {
        cout << "<return>" << endl;
        std::cin.get();
        data = 42;
        readyFlag.store(true);
    }
    void consumer() {
        while (!readyFlag.load()) {
            std::cout.put('.').flush();
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
        }
        cout << "\nvalue:" << data << std::endl;
    }
}
void test13() {
    auto p = std::async(std::launch::async, AtomicityTest::provider);
    auto c = std::async(std::launch::async, AtomicityTest::consumer);
}
int main()
{
    //test1();
    //test2();
    //test3();
    //test4();
    //test5();
    //test6();
    // test7();
    // test8();
    //test9();
    //test11();
    //test12();
    test13();
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值