首页 > 编程语言 >C++多线程:thread

C++多线程:thread

时间:2024-11-01 20:00:42浏览次数:3  
标签:std 多线程 thread 对象 C++ 死锁 线程 include

进程与线程

进程:系统资源分配的最小单元,通常被定义为一个正在运行的程序实例
线程:系统任务调度的最小单元

进程间通信:管道,信号量,信号,消息队列,共享内存,套接字
线程间通信:锁机制,信号量机制,信号机制,屏障

同步:保证任务片段的先后顺序
互斥:为了保证资源在同一时刻只能被一个线程使用,即数据一致性

同步方式

  • 原子操作
  • 信号量(semaphore)
  • 读写信号量(rw_semaphore)
  • 互斥锁
  • 自旋锁(spinlock)
  • 读写锁(rwlock)
  • 顺序锁(seqlock)

有序资源分配法
银行家算法,死锁
生产者消费者问题
读者写者问题
哲学家就餐问题,互斥

C++多线程编程涉及的头文件如下

<thread>
<mutex>
<condition_variable>

<future>
<promise>
<packaged_task>

<atomic>

<async>

死锁

产生死锁的四个必要条件

条件 说明
互斥 资源同一时刻只能被一个进程使用
请求并保持 进程在请资源时,不释放自己已经占有的资源
不剥夺 进程已经获得的资源,在进程使用完前,不能强制剥夺
循环等待 进程间形成环状的资源循环等待关系

死锁预防

破坏死锁产生的四个条件

  • 打破互斥条件
  • 打破不剥夺条件
  • 打破请求并保持条件
  • 打破循环等待条件

死锁避免

对分配资源做安全性检查,确保不会产生循环等待,如银行家算法

死锁检测

允许死锁的发生,但提供检测方法

死锁解除

已经产生死锁,强制剥夺资源或撤销线程

C++11线程库

C++11中定义了标准的线程库<thread>,位于std命名空间,编译时添加参数-std=c++11

构造函数

  • 默认构造函数
    thread()noexcept;
    创建一个空的thread对象,该对象是不可joinable的
  • 初始化构造函数
    template<typename Fn, typename... Args>
    explicit thread(Fn&& fn, Args&&... args);
    
    创建一个可joinablethread对象
    新产生的线程会调用fn(),实参由args给出

拷贝构造函数

thread(const thread&)=delete;

被禁用,thread对象不可拷贝

移动构造函数

thread(thread&& x) noexcept;

将一个临时(匿名的)thread对象赋值给另一个thread对象,调用成功后,x将不代表任何thread对象

注意,每个thread对象在销毁(析构)前,要么调用join()让主线程等待子线程执行完成
要么调用detach()将子线程和主线程分离,两者必选其一,否则程序可能存在以下两个问题:

  • 线程占用的资源将无法全部释放,造成内存泄漏
  • 当主线程执行完成而子线程未执行完时,程序执行将引发异常

示例如下:

#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>
 
void f1(int n) {
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread " << n << " executing\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));
    }
}
 
int main() {
    int n = 0;
    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
    t2.join();
    t4.join();
    std::cout << "Final value of n is " << n << '\n';
}

赋值操作

  • 拷贝赋值
    thread& operator=(const thread&)=delete;
    
    被禁用,thread对象不可拷贝
  • 移动赋值
    thread& operator=(thread&& rhs)noexcept;
    
    若被赋值对象是非joinable的,则需要传递一个右值引用rhs,否则terminate()报错

示例如下:

#include <stdio.h>
#include <stdlib.h>

#include <chrono>    // std::chrono::seconds
#include <iostream>  // std::cout
#include <thread>    // std::thread, std::this_thread::sleep_for

void thread_task(int n) {
    std::this_thread::sleep_for(std::chrono::seconds(n));
    std::cout << "hello thread "
              << std::this_thread::get_id()
              << " paused " << n << " seconds." << std::endl;
}

int main(int argc, char *argv[]) {
    std::thread threads[5];
    std::cout << "Spawning 5 threads...\n";
    for (int i = 0; i < 5; i++) {
        threads[i] = std::thread(thread_task, i+1);
    }
    std::cout << "Done spawning threads! Now wait for them to join\n";
    for (auto& t: threads) {
        t.join();
    }
    std::cout << "All threads joined.\n";

    return EXIT_SUCCESS;
}

其他成员函数

函数 说明
joinable() 判断线程对象是否支持调用join函数
join() 阻塞线程对象所在的当前线程,直到线程对象执行完毕
detach() 将线程对象从该对象所在线程分离,使其彼此独立执行
swap() 交换两个线程对象所代表的底层句柄underlying handlers
native_handle() 返回与std::thread的具体实现相关的线程句柄
hardware_concurrency()[static] 检测硬件并发特性,返回当前平台的线程实现所支持的线程并发数目,但返回值仅仅只作为系统提示(hint)

注意

  1. 若某个线程已经执行完任务,但没有被join的话,该线程依然会被认为是一个活动的执行线程,因此也是可被join的
  2. 调用detach()
    • *this不再代表任何线程实例
    • joinable()==false
    • std::this_thread::get_id()==std::thread::id()

std::this_thread 命名空间中相关辅助函数

<thread>头文件中不仅定义了thread类,还提供了一个名为this_thread的命名空间,此空间中包含一些功能实用的函数

函数 说明
get_id() 获取当前线程对象id
yield() 阻塞当前线程,操作系统调度执行另一线程
sleep_until() 阻塞当前线程,直到某个时间点
sleep_for() 阻塞线程休眠指定的时间片time span,由于线程调度等原因,实际休眠时间可能比sleep_duration更长

示例如下:

#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>

void f1(int n) {
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread " << n << " executing\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));
    }
}

int main() {
    int n = 0;
    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
    // 阻塞主线程,等待t2执行完毕
    t2.join();
    // 阻塞主线程,等待t4执行完毕
    t4.join();
    std::cout << "Final value of n is " << n << '\n';
}

标签:std,多线程,thread,对象,C++,死锁,线程,include
From: https://www.cnblogs.com/sgqmax/p/18521159

相关文章

  • C++多线程:mutex
    互斥量C++11互斥锁定义在<mutex>头文件中,提供了独占资源的特性C++11头文件中定义的互斥量互斥量说明mutex基本互斥量recursive_mutex递归互斥量timed_mutex定时互斥量recursive_timed_mutex递归定时互斥量std::mutex最基本的互斥量,提供了独占所有权......
  • C++多线程:condition_variable
    条件变量类似于pthread库中的pthread_cond_*()提供的功能,C++11标准提供了两种表示条件变量的类,分别是condition_variable和condition_variable_any,定义在头文件<condition_variable>中std::condition_variable当std::condition_variable对象调用wait()时,会阻塞当前线程,直到该s......
  • C++多线程:promise
    头文件包含:Providers类std::promisestd::packaged_taskFutures类std::futurestd::shared_futureProviders函数std::async()其他类型std::future_errorstd::future_errcstd::future_statusstd::launchstd::promise用来保存某一类型T的值,该值可以被future对......
  • C++多线程:package_task
    std::packaged_taskstd::packaged_task包装一个可调用对象,并允许获取该可调用对象计算的结果,可调用对象内部包含两个基本元素:1.被包装的任务任务是一个可调用对象,如函数指针或函数对象,该对象的执行结果会传递给共享状态2.共享状态用于保存任务的返回结果,并可通过future对象异......
  • C++标准库:chrono
    ratio先看一下ratio的定义template<intmax_tN,intmax_tD=1>classratio;ratio是由非类型参数定义的模板,用来定义一个比率N/D,如ratio<1,-2>表示-0.5标准库中定义的常用ratio类型typedefinition说明ratio<1,1000>std::milli1/1000ratio<1,1000000>std::mic......
  • C++标准库:random
    随机数生成设备随机数生成设备random_device,生成非确定性随机数,在Linux中通过读取/dev/urandom设备,Windows中使用rand_s重载了()运算符,每次调用会生成一个min()到max()之间的高质量随机数种子,若在Linux(UnixLike)下,可以使用这个生成高质量的随机数,可以理解为真随机数,windows下......
  • Chromium 中chrome.topSites扩展接口定义c++
    一、chrome.topSites使用 chrome.topSites API访问新标签页上显示的热门网站(即最常访问的网站)。不包括用户自定义的快捷方式。权限topSites您必须声明“topSites”扩展程序清单中授予使用此API的权限。{ "name":"Myextension", ... "permissions":[ ......
  • 《保卫萝卜》客户端缺失pthreadvce2.dll 文件?详解《保卫萝卜》Luobo.exe 加载 pthread
    在享受《保卫萝卜》这款趣味横生的塔防游戏时,部分玩家可能会遇到游戏无法启动的问题,屏幕上弹出错误提示:“由于找不到pthreadvce2.dll,无法继续执行代码。”这一错误通常意味着你的电脑系统中缺失了pthreadvce2.dll这个关键的动态链接库(DLL)文件。别担心,本文将为你提供详细的修......
  • Android使用timer和thread实现定时器
    说明:两种方法实现android定时器,定时执行任务第一种方式:step1:packagecom.example.iosdialogdemo;importandroid.os.Bundle;importandroid.os.Handler;importandroidx.appcompat.app.AppCompatActivity;importjava.util.Timer;publicclassTimerActivityextends......
  • ThreadLocal的使用以及原理
    ThreadLocal的使用以及原理  概要  ThreadLocal是java提供的一个方便对象在本线程内不同方法中进行传递和获取的类。用它定义的变量,仅在本线程中可见和维护,不受其他线程的影响,与其他线程相互隔离。  一、ThreadLocal能解决什么问题?  当涉及一个对象需要在很多......