Win32 多线程程序设计:线程的第一次接触(零基础入门)

🌹 作者:云小逸

🤟 个人主页: 云小逸的主页

🤟 motto: 要敢于一个人默默的面对自己,强大自己才是核心。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前,其次就是现在!学会自己和解,与过去和解,努力爱自己。希望春天来之前,我们一起面朝大海,春暖花开!

🥇 专栏:

文章目录

📚 前言

对于零基础学习 Win32 编程的同学来说,“多线程” 可能是第一个让人困惑的概念 —— 为什么不能用单线程写完所有功能?多线程到底能解决什么问题?本章会从 “实际场景” 出发,先帮你搞懂多线程的核心价值,再一步步拆解线程的基本概念、创建方法、常见问题及实战应用,全程搭配通俗解释和可运行代码,让你从 “完全不懂” 到 “能写出第一个多线程程序”。

一、为什么要学多线程?—— 先搞懂 “为什么需要它”

在学具体技术前,我们先解决一个核心问题:为什么需要多线程?

想象一个场景:你用软件编辑文档,点击 “打印” 后,软件开始生成打印数据。如果是单线程程序,这时候你不能再编辑文档 —— 软件会 “卡住”,直到打印数据生成完。但多线程能让 “生成打印数据” 和 “编辑文档” 同时进行:一个线程(worker 线程)默默处理打印,另一个线程(GUI 线程)响应用户的编辑操作。

简单说,多线程能让程序 “同时做多个事”,避免因一个耗时操作(如打印、文件读取)卡住整个界面,提升用户体验和程序效率

二、线程的基本概念 —— 先分清 “线程” 和 “进程”

在 Win32 环境中,“进程” 和 “线程” 是两个极易混淆的概念,我们用 “容器” 和 “执行者” 的关系来理解:

  • 进程(Process):是程序的 “容器”,包含程序运行所需的内存空间、资源(如窗口、文件句柄),比如你打开的 “记事本” 就是一个进程;

  • 线程(Thread):是进程内的 “执行单元”,CPU 真正调度和执行的是线程,比如记事本的 “编辑文字” 和 “自动保存” 就是两个独立的线程。

1. 进程与线程的核心关系

  • 一个进程至少有一个线程(称为 “主线程”,程序启动后默认执行的线程);

  • 多个线程共享进程的内存和资源(如全局变量、文件句柄),但每个线程有自己的 “栈”(存储局部变量,互不干扰)。

2. 通俗示例

打开 “微信”(一个进程),里面的 “聊天窗口接收消息”“朋友圈加载图片”“文件传输” 就是三个独立的线程 —— 它们共享微信的内存(你的账号信息、聊天记录),但各自独立执行,不会因为 “文件传输” 而导致 “接收消息” 卡住。

三、产生第一个线程 ——CreateThread 函数详解

要创建线程,Win32 提供了CreateThread函数,这是多线程编程的 “入门钥匙”。我们从函数原型、参数含义、线程函数格式三个维度讲透,确保你能独立调用。

1. CreateThread 函数原型与参数

HANDLE CreateThread(

   LPSECURITY_ATTRIBUTES lpThreadAttributes,  // 安全属性(一般传NULL)

   DWORD dwStackSize,                         // 线程栈大小(0=默认1MB)

   LPTHREAD_START_ROUTINE lpStartAddress,     // 线程函数地址(核心)

   LPVOID lpParameter,                        // 传给线程的参数(任意数据)

   DWORD dwCreationFlags,                     // 创建标志(0=立即执行)

   LPDWORD lpThreadId                         // 输出线程ID(不能传NULL,Win95不支持)

);

2. 参数通俗解释(零基础必看)

参数名 作用 零基础理解示例
lpThreadAttributes 线程的安全权限(如是否允许子进程继承),Windows 95 忽略,直接传 NULL 不用管,填 NULL 就行。
dwStackSize 线程栈的大小(单位:字节),0 表示用默认值(1MB),一般传 0 线程的 “临时工作空间”,默认够大,填 0。
lpStartAddress 线程要执行的函数地址(必须是特定格式的函数),核心参数 线程启动后要 “干活的函数”。
lpParameter 传给线程函数的参数(可以是任意数据,用 LPVOID 强制转换)。 给 “干活函数” 的 “工具”(如打印内容)。
dwCreationFlags 线程创建后是否立即执行:0 = 立即执行;CREATE_SUSPENDED = 暂停执行。 一般填 0,让线程马上开始干活。
lpThreadId 输出参数,存储新线程的 ID(唯一标识系统中的线程),必须传有效指针 线程的 “身份证号”,存到变量里。

3. 返回值说明

  • 成功:返回一个 HANDLE(线程句柄),代表对 “线程核心对象” 的引用;

  • 失败:返回 NULL,调用 GetLastError() 可查看失败原因(如内存不足)。

四、线程函数的 “强制格式”—— 必须这么写!

CreateThread对 “线程要执行的函数” 有严格要求,格式错了会编译失败或程序崩溃,这是零基础最容易踩的坑,一定要牢记。

1. 强制格式

// 格式:返回值 DWORD,调用约定 WINAPI,参数 LPVOID

DWORD WINAPI 线程函数名(LPVOID 传入参数) {

   // 线程要做的事情

   return 0;  // 线程结束代码(传给 GetExitCodeThread)

}

2. 为什么要这个格式?

因为CreateThread是 Win32 API,它要求线程函数必须按照这个 “约定” 传递参数和返回值 —— 就像快递员必须按地址送包裹,格式不对,CPU 无法正确 “接收” 线程的执行结果,程序会出错。

3. 示例:最简单的线程函数

// 线程函数:接收一个整数参数,打印10次该整数

DWORD WINAPI PrintNumThread(LPVOID param) {

   int num = (int)param;  // 将 LPVOID 转换为实际类型(这里是int)

   for (int i = 0; i < 10; i++) {

       printf("%d", num);  // 打印参数

   }

   printf("n");

   return num  10;  // 线程结束代码(示例:返回参数的10倍)

}

五、实战:产生第一个多线程程序

我们写一个完整程序:启动 5 个线程,每个线程打印自己的参数(0~4)10 次,通过代码实操理解多线程的执行逻辑。

1. 完整代码(带详细注释)

#define WIN32_LEAN_AND_MEAN  // 减少头文件冗余

#include <stdio.h>

#include <windows.h>

// 1. 声明线程函数(必须符合格式)

DWORD WINAPI PrintNumThread(LPVOID param);

int main() {

   HANDLE hThread;  // 线程句柄

   DWORD threadId;  // 线程ID

   int i;

   // 2. 循环创建5个线程

   for (i = 0; i < 5; i++) {

       // 调用 CreateThread 创建线程

       hThread = CreateThread(

           NULL,           // 安全属性:默认

           0,              // 栈大小:默认

           PrintNumThread, // 线程函数:PrintNumThread

           (LPVOID)i,      // 传给线程的参数:当前循环变量i

           0,              // 创建标志:立即执行

           &threadId       // 输出线程ID到threadId

       );

       // 检查是否创建成功

       if (hThread != NULL) {

           printf("成功启动线程,参数:%d,线程ID:%lun", i, threadId);

           // 3. 释放线程句柄(避免资源泄漏)

           CloseHandle(hThread);

       } else {

           printf("创建线程 %d 失败!n", i);

       }

   }

   // 4. 等待子线程结束(简单方法:Sleep 2秒,实际项目用WaitForSingleObject)

   Sleep(2000);

   return 0;

}

// 5. 实现线程函数

DWORD WINAPI PrintNumThread(LPVOID param) {

   int num = (int)param;  // 转换参数类型

   for (int i = 0; i < 10; i++) {

       printf("%d", num);

   }

   return 0;

}

2. 编译与运行注意事项

  • 编译时需指定 “多线程 C 运行时库”,否则printf可能输出混乱:

    用 Visual Studio 编译:项目属性 → C/C++ → 代码生成 → 运行时库 → 选择 “多线程 (/MT)” 或 “多线程 DLL (/MD)”;

    用命令行编译:cl /MT 程序名.c

  • 运行结果:每次执行的打印顺序可能不同(如先打印 2 的线程,再打印 0 的线程),这是 CPU 调度的正常现象,不用慌!

六、多线程的 “坑”—— 执行结果的无序性与竞争条件

刚接触多线程时,很多同学会疑惑:“为什么每次运行结果都不一样?” 这其实是多线程的正常特性,也是需要解决的 “坑”。

1. 无序性的原因

Win32 是 “抢先式多任务”——CPU 会在多个线程间快速切换(称为 “上下文切换”),每个线程只执行一小段时间(“时间片”)。比如两个线程同时执行printf,可能线程 A 打印了一半,CPU 突然切换到线程 B,导致输出混乱(如预期 “000111” 变成 “010101”)。

2. 示例:竞争条件(Race Condition)

// 问题代码:两个线程同时打印,输出混乱

DWORD WINAPI ThreadA(LPVOID param) {

   for (int i = 0; i < 5; i++) printf("A");

   return 0;

}

DWORD WINAPI ThreadB(LPVOID param) {

   for (int i = 0; i < 5; i++) printf("B");

   return 0;

}

int main() {

   CreateThread(NULL, 0, ThreadA, NULL, 0, NULL);

   CreateThread(NULL, 0, ThreadB, NULL, 0, NULL);

   Sleep(1000);

   return 0;

}
  • 可能的输出:AABABABBB(而非预期的AAAAABBBBB),因为两个线程 “抢着” 写控制台。

  • 解决方法:使用多线程 C 运行时库(/MT 或 /MD),或用 “同步机制”(如临界区)保护printf(后续章节详解)。

七、线程的 “身份证” 与 “借书卡”—— 线程 ID、句柄与核心对象

在 Win32 中,线程有两个关键标识:线程 ID线程句柄(HANDLE),它们的作用完全不同,必须分清,否则会导致代码出错。

1. 线程 ID(Thread ID)

  • 是一个 DWORD 类型的整数,全局唯一(系统中所有线程的 ID 不重复);

  • 用途:标识线程(如调试时区分不同线程),调用PostThreadMessage(线程间发消息)需要线程 ID;

  • 局限性:无法通过 ID 直接操作线程(如等待线程结束),必须用 “句柄”。

2. 线程句柄(Thread Handle)

  • 是 “线程核心对象(Kernel Object)” 的引用,类似 “图书馆的借书卡”—— 持有句柄,就有操作线程的权限(如等待线程结束、获取结束代码);

  • 核心特性:核心对象有 “引用计数”—— 线程创建时,引用计数默认是 2(一个来自CreateThread返回的句柄,一个来自线程本身);

  • 关键注意:必须调用CloseHandle释放句柄,否则会导致 “资源泄漏”(即使线程结束,核心对象也不会销毁,占用内存)。

3. 引用计数的通俗理解

用 “图书馆借书” 类比:

  • 你借走《Win32 多线程》(CreateThread),获得一张借书卡(句柄),引用计数 = 1;

  • 图书馆为了跟踪书籍,自己留了一张记录(线程本身持有),引用计数 = 2;

  • 你还书(CloseHandle),引用计数 = 1;

  • 你看完书还回图书馆(线程结束),引用计数 = 0,图书馆销毁书籍记录(核心对象销毁)。

4. 实战:正确释放句柄

int main() {

   HANDLE hThread = CreateThread(NULL, 0, PrintNumThread, (LPVOID)1, 0, &threadId);

   if (hThread != NULL) {

       // 必须调用 CloseHandle 释放句柄

       CloseHandle(hThread);

       hThread = NULL;  // 避免野指针(防止后续误操作)

   }

   Sleep(1000);

   return 0;

}

八、如何知道线程 “干完活了”?—— 线程结束代码与 GetExitCodeThread

有时候我们需要判断线程是否还在运行,或获取线程的执行结果(如线程计算出的数值),这就需要GetExitCodeThread函数。

1. GetExitCodeThread 函数原型

BOOL GetExitCodeThread(

   HANDLE hThread,    // 线程句柄(必须是有效的)

   LPDWORD lpExitCode // 输出:线程的结束代码

);

2. 参数与返回值说明

  • hThread:线程的句柄(CreateThread返回的,且未被CloseHandle释放);

  • lpExitCode:输出参数,存储线程的结束代码:

    • 线程仍在运行:lpExitCode会被设为STILL_ACTIVE(值为 0x00000103);

    • 线程已结束:lpExitCode会被设为线程函数的返回值(如线程函数return 10,则为 10);

  • 返回值:成功返回 TRUE,失败返回 FALSE(如句柄无效)。

3. 实战:检查线程是否结束

启动两个线程,等待用户按任意键后,检查线程状态:

#include <conio.h>  // 用于 getch()(等待用户按键)

DWORD WINAPI SlowThread(LPVOID param) {

   int num = (int)param;

   Sleep(num  1000);  // 线程1睡1秒,线程2睡2秒

   return num  10;    // 结束代码:10(线程1)或20(线程2)

}

int main() {

   HANDLE hThrd1, hThrd2;

   DWORD exitCode1, exitCode2;

   DWORD threadId;

   // 启动两个线程

   hThrd1 = CreateThread(NULL, 0, SlowThread, (LPVOID)1, 0, &threadId);

   hThrd2 = CreateThread(NULL, 0, SlowThread, (LPVOID)2, 0, &threadId);

   // 等待用户按键

   printf("按任意键检查线程状态...n");

   getch();

   // 检查线程1状态

   if (GetExitCodeThread(hThrd1, &exitCode1)) {

       if (exitCode1 == STILL_ACTIVE) {

           printf("线程1还在运行!n");

       } else {

           printf("线程1已结束,结束代码:%lun", exitCode1);

       }

   }

   // 检查线程2状态

   if (GetExitCodeThread(hThrd2, &exitCode2)) {

       if (exitCode2 == STILL_ACTIVE) {

           printf("线程2还在运行!n");

       } else {

           printf("线程2已结束,结束代码:%lun", exitCode2);

       }

   }

   // 释放句柄

   CloseHandle(hThrd1);

   CloseHandle(hThrd2);

   return 0;

}

4. 运行结果分析

  • 1 秒内按按键:线程 1(睡 1 秒)和线程 2(睡 2 秒)都在运行,输出 “线程 1 还在运行!”“线程 2 还在运行!”;

  • 2 秒后按按键:线程 1 和线程 2 都已结束,输出 “线程 1 已结束,结束代码:10”“线程 2 已结束,结束代码:20”。

九、如何 “强制结束” 线程?——ExitThread 函数

线程结束有两种方式,一种是 “自然结束”,一种是 “强制结束”,我们分别讲解。

1. 线程结束的两种方式

  • 自然结束:线程函数执行到return语句,正常退出;

  • 强制结束:调用ExitThread函数,直接终止线程(调用后不会返回,后续代码不执行)。

2. ExitThread 函数原型

VOID ExitThread(DWORD dwExitCode);
  • 参数dwExitCode:线程的结束代码(会被GetExitCodeThread获取);

  • 核心特点:调用后立即终止线程,函数本身不返回,线程内ExitThread之后的代码永远不会执行。

3. 示例:调用 ExitThread 结束线程

DWORD WINAPI TestExitThread(LPVOID param) {

   printf("线程开始执行...n");

   ExitThread(4);  // 强制结束,结束代码4

   printf("这里的代码不会执行!n");  // 永远不会打印(线程已终止)

   return 0;

}

int main() {

   HANDLE hThread = CreateThread(NULL, 0, TestExitThread, NULL, 0, &threadId);

   DWORD exitCode;

   if (hThread != NULL) {

       // 等待线程结束(后续章节详解WaitForSingleObject)

       WaitForSingleObject(hThread, INFINITE);

       GetExitCodeThread(hThread, &exitCode);

       printf("线程结束代码:%lun", exitCode);  // 输出4

       CloseHandle(hThread);

   }

   return 0;

}

4. 关键注意:主线程结束的影响

主线程(main 函数所在线程)结束会强制所有子线程结束,即使子线程还在运行。比如:

int main() {

   // 子线程睡5秒(本应执行5秒)

   CreateThread(NULL, 0, SlowThread, (LPVOID)5, 0, NULL); 

   // 主线程直接返回,强制子线程结束

   return 0;

}
  • 运行结果:子线程刚启动就被终止,不会执行完 5 秒的Sleep

  • 解决方法:主线程必须等待所有子线程结束后再退出(用WaitForSingleObjectWaitForMultipleObjects,后续章节详解)。

十、多线程的 “安全网”—— 错误处理与 MTVERIFY 宏

Win32 函数(如CreateThreadCloseHandle)可能失败(比如内存不足、句柄无效),如果不检查错误,程序会莫名崩溃,调试时找不到原因。第二章推荐使用MTVERIFY 宏,它能自动检查函数返回值,失败时打印详细错误信息。

1. MTVERIFY 宏的原理

宏的核心逻辑:调用 Win32 函数 → 检查返回值 → 失败时获取错误原因并打印(包含函数名、行号、错误码)。

宏定义在MtVerify.h中(附录 A 有完整代码),基本格式如下:

#define MTVERIFY(expr) 

do { 

   BOOL _success = (expr); 

   if (!_success) { 

       DWORD _err = GetLastError(); 

       printf("在文件 %s 第 %d 行,调用 %s 失败!n原因:%lun", 

              __FILE__, __LINE__, #expr, _err); 

   } 

} while(0)

2. 实战:用 MTVERIFY 检查错误

#include "MtVerify.h"  // 包含MTVERIFY宏

int main() {

   DWORD threadId, exitCode;

   // 检查线程创建是否成功

   HANDLE hThread = CreateThread(NULL, 0, PrintNumThread, (LPVOID)1, 0, &threadId);

   MTVERIFY(hThread != NULL);

   // 错误场景:释放句柄后再调用GetExitCodeThread

   MTVERIFY(CloseHandle(hThread));

   // 这里会失败(hThread已无效),MTVERIFY自动打印错误

   MTVERIFY(GetExitCodeThread(hThread, &exitCode));

   return 0;

}

3. 运行结果(错误提示)

在文件 test.c 第 12 行,调用 GetExitCodeThread(hThread, &exitCode) 失败!

原因:6(ERROR_INVALID_HANDLE,无效的句柄)

通过错误码 “6”,我们能快速定位问题 —— 句柄已被释放,无法再使用。

十一、实战项目:后台打印程序 —— 多线程的实际应用

第二章的核心实战是 “后台打印”,通过这个项目,我们学习GUI 线程与 Worker 线程的分工线程间数据传递安全等待线程结束,把前面的知识点串联起来。

1. 核心概念:GUI 线程与 Worker 线程

  • GUI 线程:负责处理用户界面(如窗口、按钮、对话框),必须有 “消息循环”(处理鼠标、键盘操作);

  • Worker 线程:负责 “后台工作”(如打印、计算、文件读取),不处理界面,避免卡住 GUI 线程;

  • 关键规则:Worker 线程不能直接创建窗口、对话框(会导致界面混乱),需通过 GUI 线程处理界面操作。

2. 后台打印程序的核心逻辑

  1. GUI 线程:显示 “编辑框” 和 “打印按钮”,用户输入文字后点击 “打印”;

  2. GUI 线程:弹出 “打印对话框”,获取打印机 DC(设备上下文,用于打印输出);

  3. GUI 线程:在堆内存中创建ThreadPrintInfo结构体,存储打印所需数据(文字、打印机 DC、窗口句柄);

  4. GUI 线程:调用CreateThread启动 Worker 线程(处理打印);

  5. Worker 线程:生成打印数据(如设置文字格式),输出到打印机;

  6. GUI 线程:等待所有 Worker 线程结束后,再允许程序退出。

3. 关键代码解析(核心部分)

(1)定义传递数据的结构体(避免全局变量)
// 存储打印所需的参数(堆内存分配,避免栈变量失效)

typedef struct {

   HWND hWndParent;  // GUI窗口句柄(用于通知界面)

   HDC hDc;          // 打印机DC(打印输出用)

   BOOL bPrint;      // 是否打印(TRUE=打印,FALSE=预览)

   char szText[256]; // 要打印的文字(用户输入)

} ThreadPrintInfo;
(2)GUI 线程:启动 Worker 线程(点击 “打印” 按钮时调用)
void OnPrint(HWND hWnd, char szText) {

   PRINTDLG dlgPrint = {0};

   dlgPrint.lStructSize = sizeof(PRINTDLG);

   dlgPrint.hwndOwner = hWnd;

   dlgPrint.Flags = PD_RETURNDC;  // 让PrintDlg返回打印机DC

   // 1. 弹出打印对话框,获取打印机DC

   if (!PrintDlg(&dlgPrint)) {

       return;

   }

   // 2. 在堆内存中分配ThreadPrintInfo(避免栈变量失效)

   ThreadPrintInfo pInfo = HeapAlloc(

       GetProcessHeap(),

       HEAP_ZERO_MEMORY,

       sizeof(ThreadPrintInfo)

   );

   if (pInfo == NULL) {

       MessageBox(hWnd, "内存分配失败!", "错误", MB_OK);

       return;

   }

   // 3. 初始化参数

   pInfo->hWndParent = hWnd;

   pInfo->hDc = dlgPrint.hDC;

   pInfo->bPrint = TRUE;

   strcpy(pInfo->szText, szText);  // 复制用户输入的文字

   // 4. 启动Worker线程(处理打印)

   static HANDLE gPrintThreads[10];  // 存储线程句柄(最多10个打印线程)

   static int gThreadCount = 0;      // 线程计数

   HANDLE hThread = CreateThread(

       NULL,

       0,

       BackgroundPrintThread,  // Worker线程函数

       (LPVOID)pInfo,          // 传递堆内存的参数

       0,

       &threadId

   );

   if (hThread != NULL) {

       gPrintThreads[gThreadCount++] = hThread;  // 存储句柄,后续等待

       CloseHandle(hThread);  // 释放句柄(线程仍在运行)

   } else {

       HeapFree(GetProcessHeap(), 0, pInfo);  // 创建失败,释放内存

   }

}
(3)Worker 线程:处理打印(核心工作逻辑)
// Worker线程函数:处理打印

DWORD WINAPI BackgroundPrintThread(LPVOID param) {

   ThreadPrintInfo pInfo = (ThreadPrintInfo)param;

   HDC hDc = pInfo->hDc;

   char szText = pInfo->szText;

   // 1. 初始化打印机(StartDoc:开始打印文档,StartPage:开始打印页)

   DOCINFO docInfo = {0};

   docInfo.cbSize = sizeof(DOCINFO);

   docInfo.lpszDocName = "后台打印示例";  // 打印文档名

   StartDoc(hDc, &docInfo);

   StartPage(hDc);

   // 2. 打印文字(简化为绘制文字,实际项目可设置字体、颜色)

   RECT rect = {50, 50, 500, 100};  // 打印位置(左、上、右、下)

   DrawText(hDc, szText, -1, &rect, DT_LEFT);  // 绘制文字到打印机DC

   // 3. 结束打印(EndPage:结束当前页,EndDoc:结束打印文档)

   EndPage(hDc);

   EndDoc(hDc);

   // 4. 释放资源(避免内存泄漏)

   DeleteDC(hDc);  // 释放打印机DC

   HeapFree(GetProcessHeap(), 0, pInfo);  // 释放堆内存参数

   return 0;

}
(4)GUI 线程:等待所有 Worker 线程结束(程序退出时调用)
void OnExit() {

   static HANDLE gPrintThreads[10];

   static int gThreadCount = 0;

   // 等待所有打印线程结束

   for (int i = 0; i < gThreadCount; i++) {

       DWORD exitCode;

       // 循环检查线程是否结束(避免忙等占用CPU,每10毫秒查一次)

       do {

           GetExitCodeThread(gPrintThreads[i], &exitCode);

           Sleep(10);

       } while (exitCode == STILL_ACTIVE);

       CloseHandle(gPrintThreads[i]);  // 释放句柄

   }

}

4. 关键注意事项

  • 数据传递用堆内存:避免用全局变量(多个线程可能同时修改,导致错误)和栈变量(函数返回后栈变量失效,线程访问会出错);

  • 不共享 GDI 对象:打印机 DC、画笔、画刷等 GDI 对象不能多个线程共享,否则会导致绘图错误;

  • 主线程处理界面:Worker 线程如需显示信息(如 “打印完成” 提示),需通过SendMessage通知 GUI 线程,由 GUI 线程弹出对话框。

十二、第二章核心知识点总结

  1. 线程创建:用CreateThread函数,线程函数必须是DWORD WINAPI 函数名(LPVOID)格式;

  2. 资源释放:线程句柄必须用CloseHandle释放,避免资源泄漏;

  3. 线程状态:用GetExitCodeThread检查线程是否运行(STILL_ACTIVE表示正在运行);

  4. 线程结束ExitThread强制结束线程,主线程结束会强制所有子线程结束;

  5. 错误处理:用MTVERIFY宏检查 Win32 函数错误,快速定位问题;

  6. 实战原则:GUI 线程处理界面,Worker 线程处理后台工作,数据用堆内存传递,避免共享资源。

📣 结语

感谢你耐心看完这章内容!恭喜你从 “完全不懂多线程” 到 “能理解线程概念、写出第一个多线程程序、解决简单的多线程问题”—— 这是一个不小的进步!

多线程是 Win32 编程的核心难点,但也是提升程序性能的关键技术。本章我们打下了基础,后续章节会深入 “同步机制”(解决竞争条件)、“线程优先级”“Overlapped I/O” 等高级内容。

如果你觉得这章内容对你有帮助,记得给我点赞、收藏和关注哦(。・ω・。)!让我们一起加油,从零基础新手逐渐成长为 Win32 编程高手,每一步进步都值得为自己点赞!

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐