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;
}
上述示例中,我们创建了两个线程t1和t2,使用函数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)。

1189

被折叠的 条评论
为什么被折叠?



