夜间模式暗黑模式
字体
阴影
滤镜
圆角
主题色
C++多线程

多线程

C++11提供了threadmutex等一系列的头文件, 但在windows下的mingw需要选择安装posix多线程版本的才能使用, 下载地址: sourceforge, 拉到最下面选择第一个

Linux下就没这么多问题

thread

  • 构造函数: template< class Function, class... Args > explicit thread( Function&& f, Args&&... args );: 第一个参数是一个函数, 后面的是该函数需要的参数
  • 移动构造函数: thread( thread&& other ) noexcept;
  • 复制构造函数: thread(const thread&) = delete;: 删除, 不能使用, 也就是说不能复制一个线程
  • get_id: 获取线程id
  • join: 等待线程结束
  • detach: 允许线程独立运行

参考: https://en.cppreference.com/w/cpp/thread/thread/thread

#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
 
void f1(int n)
{
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread 1 executing\n";
        ++n;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}
 
void f2(int& n)
{
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread 2 executing\n";
        ++n;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}
 
class foo
{
public:
    void bar()
    {
        for (int i = 0; i < 5; ++i) {
            std::cout << "Thread 3 executing\n";
            ++n;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
    int n = 0;
};
 
int main()
{
    int n = 0;
    foo f;
    std::thread t1; // t1 is not a thread
    std::thread t2(f1, n + 1); // pass by value
    std::thread t3(f2, std::ref(n)); // pass by reference
    std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread
    std::thread t5(&foo::bar, &f); // t5 runs foo::bar() on object f
    t2.join();
    t4.join();
    t5.join();
    std::cout << "Final value of n is " << n << '\n';
    std::cout << "Final value of foo::n is " << f.n << '\n';
}

this_thread名称空间

std::this_thread::是一个名称空间, 包含了一些有用的函数

  • get_id: 获取当前线程的id

    std::cout << std::this_thread::get_id() << std::endl;
    
  • sleep_for: 当前线程休眠一段时间, 时间长度要用chrono中的函数, 这些函数返回一个duration对象代表指定的时间长度

    std::this_thread::sleep_for(std::chrono::seconds(2));
    
  • sleep_until: 睡眠直到指定时间, 这个指定时间也要用chrone中的函数得到一个time_point对象

  • yield: 让出CPU时间片

mutex

互斥量类, 有这么几种:

  • mutex: 最基本的互斥量

    • lock: 请求, 如果没有成功则线程阻塞直到成功
    • unlock: 释放
    • try_lock:请求, 如果成功返回true, 否则直接返回false而不会阻塞
  • recursive_mutex: 可递归, 和其他语言里面的一样

  • time_mutex: 可定时, 多了下面这两个函数

    • try_lock_for: 和try_lock一样, 不过会等待一段时间, 如果时间过了还是失败, 返回false

    • try_lock_until: 等待知道指定的时间点到来:

      void f()
      {
          auto now=std::chrono::steady_clock::now();
          test_mutex.try_lock_until(now + std::chrono::seconds(10));
          std::cout << "hello world\n";
      }
      
  • recursive_timed_mutex

其他

lock_guard类

一个类, 构造时需要传入一个mutex, 构造的时候对其上锁, 析构的时候释放, 使用的时候只需要定义一个lock_guard变量, 然后代码块结束的时候lock_guard就会自动析构达到自动释放的目的:

void safe_increment()
{
    std::lock_guard<std::mutex> lock(g_i_mutex);
    ++g_i;
 
    std::cout << std::this_thread::get_id() << ": " << g_i << '\n';
 
    // g_i_mutex is automatically released when lock
    // goes out of scope
}

lock_guard类还有一个构造函数, 可以提供一个lock tag:

lock标记

  • defer_lock_t: do not acquire ownership of the mutex
  • try_to_lock_ttry to acquire ownership of the mutex without blocking
  • adopt_lock_tassume the calling thread already has ownership of the mutex
 std::lock_guard<std::mutex> lock1(from.m, std::adopt_lock);

scoped_lock

构造函数可以传入多个互斥量

https://en.cppreference.com/w/cpp/thread/scoped_lock/scoped_lock

unique_lock

unique_locklock_guard更加灵活, 提供了try_lock, lock_for, lock_until等接口, 析构时会根据锁的状态自动判断是否需要释放锁

unique_lock只允许一个线程获取锁

提供了这些构造函数:

详见: https://en.cppreference.com/w/cpp/thread/unique_lock/unique_lock

unique_lock() noexcept;
unique_lock( unique_lock&& other ) noexcept;
explicit unique_lock( mutex_type& m );
unique_lock( mutex_type& m, std::defer_lock_t t ) noexcept;
unique_lock( mutex_type& m, std::try_to_lock_t t );
unique_lock( mutex_type& m, std::adopt_lock_t t );
template< class Rep, class Period >
unique_lock( mutex_type& m, 
             const std::chrono::duration<Rep,Period>& timeout_duration );
template< class Clock, class Duration >
unique_lock( mutex_type& m, 
             const std::chrono::time_point<Clock,Duration>& timeout_time );

shared_lock

共享锁, 用来解决读者写着问题, 可以被多个线程获取, 需要头文件shared_mutex

#include <shared_mutex>
#include <iostream>
#include <thread>
#include <chrono>
 
std::shared_timed_mutex m;
int i = 10;
 
void read()
{
   // both the threads get access to the integer i
   std::shared_lock<std::shared_timed_mutex> slk(m);
   std::cout << "read i as " << i << "...\n"; // this is not synchronized
   std::this_thread::sleep_for(std::chrono::milliseconds(10));
   std::cout << "woke up...\n";
}
 
int main()
{
   std::thread r1(read);
   std::thread r2(read);
 
   r1.join();
   r2.join();
   return 0;
}

lock和try_lock函数

函数可以传入多个互斥量并对其进行同时上锁, 使用死锁避免算法避免死锁

condition_value

头文件condition_variable

和Java和Python的差不多, 贴个例子:

https://en.cppreference.com/w/cpp/thread/condition_variable

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
 
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
 
void worker_thread()
{
    // Wait until main() sends data
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, []{return ready;});
 
    // after the wait, we own the lock.
    std::cout << "Worker thread is processing data\n";
    data += " after processing";
 
    // Send data back to main()
    processed = true;
    std::cout << "Worker thread signals data processing completed\n";
 
    // Manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    cv.notify_one();
}
 
int main()
{
    std::thread worker(worker_thread);
 
    data = "Example data";
    // send data to the worker thread
    {
        std::lock_guard<std::mutex> lk(m);
        ready = true;
        std::cout << "main() signals data ready for processing\n";
    }
    cv.notify_one();
 
    // wait for the worker
    {
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return processed;});
    }
    std::cout << "Back in main(), data = " << data << '\n';
 
    worker.join();
}

参考

C++11 并发指南

Thread support library

暂无评论

发送评论 编辑评论


				
上一篇
下一篇