美文网首页
C++11 标准库 std::thread 多线程使用教程

C++11 标准库 std::thread 多线程使用教程

作者: ImWiki | 来源:发表于2021-07-10 19:45 被阅读0次

    从 C++11 开始,标准库里已经包含了对线程的支持,std::thread是C++11标准库中的多线程的支持库,pthread.h 是标准库没有添加多线程之前的在Linux上用的多线程库。std::thread 是面向对象的多线程库,使用简单,推荐在项目中使用 std::thread 代替 pthread.h。

    修改 CMakeLists.txt

    项目中用到了C++ 17的时间代码风格,需要修改为对应的版本。

    # CMakeLists.txt
    set(CMAKE_CXX_STANDARD 17)
    

    创建线程

    #include <iostream>
    #include <thread>
    
    using namespace std;
    
    void SayHello() {
        cout << "Hello World" << endl;
    }
    
    int main() {
        std::thread t1(SayHello);
        // 等待子线程结束才退出当前线程
        pthread_exit(nullptr);
        return 0;
    }
    

    如果不加 pthread_exit(nullptr),会报libc++abi: terminating程序终止的错误。可以通过 detach() 函数,将子线程和主线分离,子线程可以独立继续运行,即使主线程结束,子线程也不会结束。

    #include <iostream>
    #include <thread>
    using namespace std::literals::chrono_literals;
    using namespace std;
    
    void SayHello() {
        cout << "Hello World" << endl;
    }
    
    int main() {
        std::thread t1(SayHello);
        t1.detach();
        this_thread::sleep_for(10ms);
        // 低于C++17使用这行代码  this_thread::sleep_for(chrono::milliseconds(10));
        return 0;
    }
    

    传递参数

    #include <iostream>
    #include <thread>
    using namespace std::literals::chrono_literals;
    using namespace std;
    
    void SayHello(int id, string name) {
        this_thread::sleep_for(10ms);
        cout << "ID:" << id << ", Hello " << name << endl;
    }
    
    int main() {
        std::thread t1(SayHello, 1, "Wiki");
        t1.join();
        return 0;
    }
    

    线程睡眠

    using namespace std::literals::chrono_literals;
    // 让当前线程睡眠 10 毫秒
    this_thread::sleep_for(10ms);
    // 低于C++17使用这行代码  this_thread::sleep_for(chrono::milliseconds(10));
    // 让当前线程睡眠 5 秒
    this_thread::sleep_for(5s);
    

    join() 等待线程运行结束

    join() 函数可以在当前线程等待线程运行结束。

    #include <iostream>
    #include <thread>
    using namespace std::literals::chrono_literals;
    using namespace std;
    
    void SayHello() {
        this_thread::sleep_for(10ms);
        cout << "Hello World" << endl;
    }
    
    int main() {
        std::thread t1(SayHello);
        t1.join();
        return 0;
    }
    

    condition_variable / wait / notify_one

    使用 condition_variable 实现生产者和消费者的实验,通过 wait 进入线程等待,知道有其它的线程把当前线程唤醒。

    #include <iostream>
    #include <thread>
    #include <list>
    using namespace std::literals::chrono_literals;
    using namespace std;
    std::mutex g_mutex;
    condition_variable g_con;
    
    list<int> products;
    
    void test() {
        int product_id = 0;
        while (true) {
            products.push_back(++product_id);
            cout << "products 生产: " << product_id << endl;
            std::unique_lock<std::mutex> lock(g_mutex);
            // 通知消费者消费
            g_con.notify_one();
            lock.unlock();
            if (product_id > 50) {
                break;
            }
            this_thread::sleep_for(2ms);
        }
    }
    
    int main() {
        std::thread t1(test);
        while (true) {
            std::unique_lock<std::mutex> lock(g_mutex);
            if (products.empty()) {
                cout << "没有产品,等待" << endl;
                // 进入等待,知道有新产品
                g_con.wait(lock);
            } else {
                int product_id = products.front();
                products.pop_front();
                cout << "消费产品 " << product_id << endl;
                this_thread::sleep_for(2ms);
                if (product_id > 50) break;
            }
        }
        t1.join();
        return 0;
    }
    

    输出结果:

    没有产品,等待
    products 生产: 1
    消费产品 1
    products 生产: 2
    消费产品 2
    没有产品,等待
    没有产品,等待
    ...
    

    thread_local

    C++11中提供了thread_local,thread_local定义的变量在每个线程都保存一份副本,而且互不干扰,在线程退出的时候自动销毁。

    #include <iostream>
    #include <thread>
    using namespace std::literals::chrono_literals;
    using namespace std;
    thread_local int t_l_counter = 0;
    
    void test() {
        cout << "flag1 t_l_counter: " << t_l_counter << endl;
        t_l_counter = 2;
    }
    
    int main() {
        t_l_counter = 1;
        std::thread t1(test);
        t1.join();
        cout << "flag2 t_l_counter: " << t_l_counter << endl;
        return 0;
    }
    

    结果:

    flag1 t_l_counter: 0
    flag2 t_l_counter: 1
    

    同步锁

    #include <iostream>
    #include <thread>
    using namespace std;
    void test() {
        cout << "task start thread ID: " << this_thread::get_id() << endl;
        this_thread::sleep_for(10ms);
        cout << "task end thread ID: " << this_thread::get_id() << endl;
    }
    int main() {
        std::thread t1(test);
        std::thread t2(test);
        std::thread t3(test);
        t1.join();
        t2.join();
        t3.join();
        return 0;
    }
    

    运行结果:

    task start thread ID: task start thread ID: task start thread ID: 0x70000fab00000x70000fb33000
    
    0x70000fbb6000
    task end thread ID: 0x70000fab0000
    task end thread ID: task end thread ID: 0x70000fb33000
    0x70000fbb6000
    

    以上代码数据的结果是无序的,如果我们需要同一时间只有一个线程在test函数中执行代码,那么就要加锁,lock() 用于加锁,而unlock() 解锁。

    #include <iostream>
    #include <thread>
    using namespace std::literals::chrono_literals;
    using namespace std;
    std::mutex g_mutex;
    
    void test() {
        g_mutex.lock();
        cout << "task start thread ID: " << this_thread::get_id() << endl;
        this_thread::sleep_for(10ms);
        cout << "task end thread ID: " << this_thread::get_id() << endl;
        g_mutex.unlock();
    }
    int main() {
        std::thread t1(test);
        std::thread t2(test);
        std::thread t3(test);
        t1.join();
        t2.join();
        t3.join();
        return 0;
    }
    

    运行结果:

    task start thread ID: 0x70000e4f2000
    task end thread ID: 0x70000e4f2000
    task start thread ID: 0x70000e46f000
    task end thread ID: 0x70000e46f000
    task start thread ID: 0x70000e3ec000
    task end thread ID: 0x70000e3ec000
    

    除了std::mutex(非递归的互斥量),还有std::timed_mutex(带超时的非递归互斥量),std::recursive_mutex(递归互斥量)、std::recursive_timed_mutex(带超时的递归互斥量)。

    同步锁封装类

    可以创建一个 ScopeMutex 类,通过构造函数和析构函数实现加锁和解锁,ScopeMutex 的作用域只在 {} 之内加锁。

    #include <iostream>
    #include <thread>
    using namespace std::literals::chrono_literals;
    using namespace std;
    std::mutex g_mutex;
    
    class ScopeMutex {
    public:
        explicit ScopeMutex(std::mutex &mutex) {
            this->mutex = &mutex;
            this->mutex->lock();
        }
    
        ~ScopeMutex() {
            this->mutex->unlock();
        }
    
        std::mutex *mutex;
    };
    
    void test() {
        cout << "task prepare thread ID: " << this_thread::get_id() << endl;
        {
            ScopeMutex scopeMutex(g_mutex);
            cout << "task start thread ID: " << this_thread::get_id() << endl;
            this_thread::sleep_for(10ms);
            cout << "task end thread ID: " << this_thread::get_id() << endl;
        }
    
    }
    
    int main() {
        std::thread t1(test);
        std::thread t2(test);
        std::thread t3(test);
        t1.join();
        t2.join();
        t3.join();
        return 0;
    }
    

    运行结果:

    task prepare thread ID: task prepare thread ID: 0x70000d9bd0000x70000d8b7000
    task start thread ID: 0x70000d9bd000
    
    task prepare thread ID: 0x70000d93a000
    task end thread ID: 0x70000d9bd000
    task start thread ID: 0x70000d8b7000
    task end thread ID: 0x70000d8b7000
    task start thread ID: 0x70000d93a000
    task end thread ID: 0x70000d93a000
    

    相关文章

      网友评论

          本文标题:C++11 标准库 std::thread 多线程使用教程

          本文链接:https://www.haomeiwen.com/subject/kbacpltx.html