CPP(c++) 多线程

参考链接:

线程支持库:https://zh.cppreference.com/w/cpp/thread 若能懂此链接,下面都不用看

1. https://blog.csdn.net/coolwriter/article/details/79883253

2. https://blog.csdn.net/coolwriter/article/details/79884298

thread:构造线程

#include <iostream>       // std::cout  
#include <thread>         // std::thread  

void thr_function1()  
{  
    for (int i = 0; i != 10; ++i)  
    {  
        std::cout << "thread 1 print " << i << std::endl;  
    }  
}  

void thr_function2(int n)  
{  
    std::cout << "thread 1 print " << n << std::endl;  
}  

int main()  
{  
    std::thread t1(thr_function1);     // spawn new thread that calls foo()  
    std::thread t2(thr_function2, 111);  // spawn new thread that calls bar(0)  
    std::cout << "main, foo and bar now execute concurrently...\n";  
    // synchronize threads:  
    t1.join();                // pauses until first finishes 主线程等待t1线程结束 
    t2.join();               // pauses until second finishes 主线程等待t2线程结束
    std::cout << "thread 1 and htread 2 completed.\n";  
    return 0;  
}

class thread

member: http://www.cplusplus.com/reference/thread/thread/

Member types
get_id  //Thread id (public member type )
native_handle_type  //Native handle type (public member type )

Member functions:
Construct thread (public member function )
Thread destructor (public member function )
operator=   // Move-assign thread (public member function )
get_id  // Get thread id (public member function )
joinable   //Check if joinable (public member function )
join  //Join thread (public member function )
detach  //Detach thread (public member function )
swap //Swap threads (public member function )
native_handle  //Get native handle (public member function )
hardware_concurrency [static]  //Detect hardware concurrency (public static member function )

多线程变量安全

方式一: 原子操作

方式二: std::mutex 互斥量

std::mutex 互斥量: https://zh.cppreference.com/w/cpp/thread/mutex

#include <iostream>       // std::cout  
#include <thread>         // std::thread  
#include <mutex>          // std::mutex  

std::mutex mtx;           // mutex for critical section  
void print_block(int n, char c) {  
    // critical section (exclusive access to std::cout signaled by locking mtx):
    // mtx.try_lock //尝试锁定互斥,若互斥不可用,则返回  
    mtx.lock();   //锁定互斥,若互斥不可用则阻塞
    for (int i = 0; i<n; ++i) { std::cout << c; }  
    std::cout << '\n';  
    mtx.unlock();  //解开互斥      
}  
int main()  
{  
    std::thread th1(print_block, 50, '*');  
    std::thread th2(print_block, 50, '$');  
    th1.join();  
    th2.join();  
    return 0;  
}

mutex类4种

std::mutex,最基本的 Mutex 类。

std::recursive_mutex,递归 Mutex 类。

std::time_mutex,定时 Mutex 类。

std::recursive_timed_mutex,定时递归 Mutex 类。

recursive_mutex: https://zh.cppreference.com/w/cpp/thread/recursive_mutex

std::recursive_mutex 与 std::mutex一样,也是一种可以被上锁的对象,但是和 std::mutex 不同的是,std::recursive_mutex 允许同一个线程对互斥量多次上锁(即递归上锁),

来获得对互斥量对象的多层所有权,std::recursive_mutex 释放互斥量时需要调用与该锁层次深度相同次数的 unlock(),可理解为 lock() 次数和 unlock() 次数相同,除此之外,

std::recursive_mutex 的特性和 std::mutex 大致相同。

time_mutex: //https://zh.cppreference.com/w/cpp/thread/timed_mutex

std::time_mutex 比 std::mutex 多了两个成员函数:

try_lock_for(): //尝试锁定互斥,若互斥在指定的时限时期中不可用则返回

try_lock_until(): //尝试锁定互斥,若直至抵达指定时间点互斥不可用则返回

#include <iostream>       // std::cout  
#include <chrono>         // std::chrono::milliseconds  
#include <thread>         // std::thread  
#include <mutex>          // std::timed_mutex  

std::timed_mutex mtx;  
void fireworks() {  
  // waiting to get a lock: each thread prints "-" every 200ms:  
  while (!mtx.try_lock_for(std::chrono::milliseconds(200))) {  
    std::cout << "-";  
  }  
  // got a lock! - wait for 1s, then this thread prints "*"  
  std::this_thread::sleep_for(std::chrono::milliseconds(1000));  
  std::cout << "*\n";  
  mtx.unlock();  
}

int main ()  
{  
  std::thread threads[10];  
  // spawn 10 threads:  
  for (int i=0; i<10; ++i)  
    threads[i] = std::thread(fireworks);  
  for (auto& th : threads) th.join();  
  return 0;  
}

Lock 类(两种)

std::lock_guard: //https://zh.cppreference.com/w/cpp/thread/lock_guard

与 Mutex RAII 相关,方便线程对互斥量上锁。 //https://zh.cppreference.com/w/cpp/thread/lock_guard

值得注意的是,lock_guard 对象并不负责管理 Mutex 对象的生命周期,lock_guard 对象只是简化了 Mutex 对象的上锁和解锁操作,

: 方便线程对互斥量上锁,即在某个 lock_guard 对象的声明周期内,它所管理的锁对象会一直保持上锁状态;

*: 而 lock_guard 的生命周期结束之后,它所管理的锁对象会被解锁。

#include <iostream>       // std::cout  
#include <thread>         // std::thread  
#include <mutex>          // std::mutex, std::lock_guard, std::adopt_lock  
std::mutex mtx;           // mutex for critical section  
void print_thread_id(int id) {  
    mtx.lock();  
    std::lock_guard<std::mutex> lck(mtx, std::adopt_lock);  //= mtx.lock() 且在lck 析构时,mtk.unlock 
    std::cout << "thread #" << id << '\n';  
}  
int main()  
{  
    std::thread threads[10];  
    // spawn 10 threads:  
    for (int i = 0; i<10; ++i)  
        threads[i] = std::thread(print_thread_id, i + 1);  

    for (auto& th : threads) th.join();  

    return 0;  
}

**** scope_lock:构造时是否加锁是可选的(不加锁时假定当前线程已经获得锁的所有权),析构时自动释放锁,所有权不可转移,对象生存期内不允许手动加锁和释放锁。


std::unique_lock: //https://zh.cppreference.com/w/cpp/thread/unique_lock

与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。

类 unique_lock 是通用互斥包装器,允许延迟锁定、锁定的有时限尝试、递归锁定、所有权转移和与条件变量一同使用。

unique_lock比lock_guard使用更加灵活,功能更加强大。

使用unique_lock需要付出更多的时间、性能成本。所以能用lock_guard时,用lock_guard

class LogFile {
    std::mutex _mu;
    ofstream f;
public:
    LogFile() {
        f.open("log.txt");
    }
    ~LogFile() {
        f.close();
    }
    void shared_print(string msg, int id) {

        std::unique_lock<std::mutex> guard(_mu);//如果 guard(_mu, std::defer_lock); 表示不上锁
        //do something 1
        guard.unlock(); //临时解锁

        //do something 2

        guard.lock(); //继续上锁
        // do something 3
        f << msg << id << endl;
        cout << msg << id << endl;
        // 结束时析构guard会临时解锁
        // 这句话可要可不要,不写,析构的时候也会自动执行
        // guard.ulock();
    }

};


condition_variable

类是同步原语 //https://zh.cppreference.com/w/cpp/thread/condition_variable

能用于阻塞一个线程,或同时阻塞多个线程,直至另一线程修改共享变量(条件)并通知 condition_variable 。

当 std::condition_variable 对象的某个 wait 函数被调用的时候,它使用 std::unique_lock(通过 std::mutex) 来锁住当前线程。当前线程会一直被阻塞,直到另外一个线程在相同的 std::condition_variable 对象上调用了 notification 函数来唤醒当前线程。

#include <iostream>                // std::cout
#include <thread>                // std::thread
#include <mutex>                // std::mutex, std::unique_lock
#include <condition_variable>    // std::condition_variable

std::mutex mtx; // 全局互斥锁.
std::condition_variable cv; // 全局条件变量.
bool ready = false; // 全局标志位.

void do_print_id(int id)
{
    std::unique_lock <std::mutex> lck(mtx);
    while (!ready) // 如果标志位不为 true, 则等待...
        cv.wait(lck); // 当前线程被阻塞, 当全局标志位变为 true 之后,此外还有 wait for ,wait until 等语句
    // 线程被唤醒, 继续往下执行打印线程编号id.
    std::cout << "thread " << id << '\n';
}

void go()
{
    std::unique_lock <std::mutex> lck(mtx);
    ready = true; // 设置全局标志位为 true.
    cv.notify_all(); // 唤醒所有线程.
    //notify_one 通知一个等待的线程 
}

int main()
{
  std::thread threads[10];
  // spawn 10 threads:
  for (int i = 0; i < 10; ++i)
      threads[i] = std::thread(do_print_id, i);
  std::cout << "10 threads ready to race...\n";
  go(); // go!
  for (auto & th:threads)
    th.join();
  return 0;
}

结果:

10 threads ready to race...

thread 1

thread 0

thread 2

thread 3

thread 4

thread 5

thread 6

thread 7

thread 8

thread 9
原文链接: https://www.cnblogs.com/heimazaifei/p/12176724.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/191728

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月12日 下午5:48
下一篇 2023年2月12日 下午5:48

相关推荐