C++多线程相关

1.多线程基础 thread

示例1: 线程函数(函数指针)


#include<iostream>
#include<thread>
using namespace std;
 
void Print1(int n, int j)
{
	cout << "n: " << n << " j: " << j << endl;
}
 
void Print2()
{
	cout << "Test2" << endl;
}
 
int main()
{
	// 线程函数为函数指针
	thread t1(Print1, 100, 5);
	thread t2(Print2);
 
	t1.join();
	t2.join();
 
	return 0;
}

 

上述示例中,我们创建了两个线程t1t2,使用函数Print1()和Print2()作为线程的执行函数,并使用join()函数等待线程执行完成。 

join() 和 detach()
当线程启动后,一定要在和线程相关联的thread销毁前,确定以何种方式等待线程执行结束。比如上例中的join。

detach方式,启动的线程自主在后台运行,当前的代码继续往下执行,不等待新线程结束。
join方式,等待启动的线程完成,才会继续往下执行。

join

join后面的代码不会被执行,除非子线程结束

void thread_1()
{
    while (1)
    {}
}
void thread_2(int x)
{
    while (1)
    {}
}
int main()
{
    thread first(thread_1); // 开启线程,调用:thread_1()
    thread second(thread_2, 100); // 开启线程,调用:thread_2(100)
 
    first.join();
    second.join(); //join完了之后,才能往下执行。
    while (1)
    {
        std::cout << "主线程\n";
    }
    return 0;
}

 

detach

 主线程不会等待子线程结束。如果主线程运行结束,程序则结束。


void thread_1()
{
    while (1)
    {
        cout << "子线程1" << endl;
    }
}
 
void thread_2(int x)
{
    while (1)
    {
        cout << "子线程2" << endl;
    }
}
 
int main()
{
    thread first(thread_1);  // 开启线程,调用:thread_1()
    thread second(thread_2, 100); // 开启线程,调用:thread_2(100)
 
    first.detach();
    second.detach();
    for (int i = 0; i < 10; i++)
    {
        std::cout << "主线程\n";
    }
    return 0;

注意:

1. 线程是在thread对象被定义的时候开始执行的,而不是在调用join()函数时才执行的,调用join()函数只是阻塞等待线程结束并回收资源。
2. 分离的线程(执行过detach()的线程)会在调用它的线程结束或自己结束时自动释放资源。
3. 线程会在函数运行完毕后自动释放,不推荐利用其他方法强制结束线程,可能会因资源未释放而导致内存泄漏。
4. 若没有执行join()或detach()的线程在程序结束时会引发异常

2.std::mutex

在多线程编程中,需要注意以下问题:

线程之间的共享数据访问需要进行同步,以防止数据竞争和其他问题。可以使用互斥量、条件变量等机制进行同步。
可能会发生死锁问题,即多个线程互相等待对方释放锁,导致程序无法继续执行。
可能会发生竞态条件问题,即多个线程执行的顺序导致结果的不确定性。


mutex头文件主要声明了与互斥量(mutex)相关的类。mutex提供了4种互斥类型,如下表所示。

lock 和 unlock

std::mutex是 C++11 中最基本的互斥量,一个线程将mutex锁住时,其它的线程就不能操作mutex,直到这个线程将mutex解锁。


#include <mutex>
 
std::mutex mtx;
int num = 0;
 
void thread_func(int& n)
{
	for (int i = 0; i < 10; ++i)
	{
		mtx.lock();
		n++;
		cout << "n: " << n << endl;;
		mtx.unlock();
	}
}
 
int main()
{
	std::thread myThread[10];
	for (std::thread& a : myThread)
	{
		a = std::thread(thread_func, std::ref(num));
		a.join();
	}
 
	std::cout << "num = " << num << std::endl;
	std::cout << "Main thread exits!" << std::endl;
	return 0;
}

3._beginthreadex

_beginthreadex()​​ 和 ​​_endthreadex()​​ 是 ​​C运行时库(CRT)​​ 提供的线程管理函数,用于创建和终止线程,并自动初始化与C运行时相关的资源(如线程局部存储、errno 等)。

​​_beginthreadex()​​:创建并启动一个新线程,返回线程句柄。
​​_endthreadex()​​:终止当前线程,释放相关资源。


uintptr_t _beginthreadex(
    void *security_descriptor,  // 安全属性(通常为NULL)
    unsigned stack_size,        // 线程栈大小(默认为1MB)
    unsigned (WINAPI *start_address)(void *), // 线程入口函数
    void *arglist,              // 传递给线程函数的参数
    unsigned initflag,          // 创建标志(如CREATE_SUSPENDED)
    unsigned *thrdaddr          // 返回线程ID

  • ​返回值​​:线程句柄(HANDLE),失败时返回 0
  • ​特点​​:
    • 自动分配线程栈空间(若 stack_size 为0)。
    • 初始化C运行时库的线程数据(如 errno、文件流、TLS)。
    • 需与 _endthreadex() 配合使用。

使用 _beginthreadex() 创建线程​

#include <process.h>
 
unsigned __stdcall ThreadFunc(void* param) {
    // 线程逻辑
    _endthreadex(0); // 显式终止线程(可选)
    return 0;
}
 
int main() {
    uintptr_t hThread = _beginthreadex(NULL, 0, ThreadFunc, NULL, 0, NULL);
    if (hThread == 0) {
        // 错误处理
    }
    WaitForSingleObject((HANDLE)hThread, INFINITE); // 等待线程结束
    CloseHandle((HANDLE)hThread);
    return 0;
}

 必须配对使用​​
若使用 _beginthreadex() 创建线程,必须在线程函数中调用 _endthreadex(),否则可能导致资源泄漏(如未释放的文件句柄)。

栈大小管理​​
默认栈大小为1MB,可通过 stack_size 参数调整。过小的栈可能导致栈溢出,过大会浪费内存。

线程函数签名​​
线程函数必须符合 unsigned __stdcall (void*) 格式,返回类型为 unsigned int。
​​

与C++的兼容性​​
_beginthreadex() 支持C++异常,若需使用C++异常,应优先选择此函数。

4.condition_variable

C++ 标准库 <condition_variable>

        用于在多线程编程中实现线程间的条件变量和线程同步。它提供了等待通知的机制,使得线程可以等待某个条件成立时被唤醒,或者在满足某个条件时通知其他等待的线程。其提供了以下几个函数用于等待和通知线程:

  • 与互斥量(<mutex>)配合使用,可避免忙等(busy-wait),在高并发情况下大幅降低 CPU 占用。

  • <condition_variable> 中主要提供两种类型:

    std::condition_variable:只能与 std::unique_lock<std::mutex> 配合使用
    std::condition_variable_any:可与任意符合 BasicLockable 的锁类型配合

std::condition_variable

class condition_variable {
public:
    condition_variable();
    ~condition_variable();

    // 禁止拷贝,可移动(C++20 起)
    condition_variable(const condition_variable&) = delete;
    condition_variable& operator=(const condition_variable&) = delete;

    // 唤醒一个等待线程
    void notify_one() noexcept;

    // 唤醒所有等待线程
    void notify_all() noexcept;

    // 等待(会自动解锁并阻塞,返回前重新加锁)
    void wait(std::unique_lock<std::mutex>& lock);

    // 带谓词重载:若谓词 false 则继续等待
    template<class Predicate>
    void wait(std::unique_lock<std::mutex>& lock, Predicate pred);

    // 带时限等待,返回 false 表示超时
    template<class Rep, class Period>
    std::cv_status wait_for(
        std::unique_lock<std::mutex>& lock,
        const std::chrono::duration<Rep,Period>& rel_time);

    template<class Rep, class Period, class Predicate>
    bool wait_for(
        std::unique_lock<std::mutex>& lock,
        const std::chrono::duration<Rep,Period>& rel_time,
        Predicate pred);

    // 绝对时刻等待,返回 false 表示超时
    template<class Clock, class Duration>
    std::cv_status wait_until(
        std::unique_lock<std::mutex>& lock,
        const std::chrono::time_point<Clock,Duration>& abs_time);

    template<class Clock, class Duration, class Predicate>
    bool wait_until(
        std::unique_lock<std::mutex>& lock,
        const std::chrono::time_point<Clock,Duration>& abs_time,
        Predicate pred);
};

wait(lock):

解锁 lock(当前线程可释放对共享数据的独占访问);
阻塞当前线程,直到被 notify 唤醒;
返回前重新对 lock 加锁;
带谓词重载:内部循环调用 wait,直到谓词返回 true,避免伪唤醒(spurious wakeup)问题。

时限版本:可指定相对时长或绝对时刻等待,超时则返回 false(或 std::cv_status::timeout)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值