[{"data":1,"prerenderedAt":526},["ShallowReactive",2],{"doc-\u002Fdocs\u002Fapi\u002Feduwin-thread-api":3},{"id":4,"title":5,"body":6,"description":55,"extension":520,"meta":521,"navigation":145,"path":522,"seo":523,"stem":524,"__hash__":525},"content\u002Fdocs\u002Fapi\u002Feduwin-thread-api.md","eduwin 线程模块 API",{"type":7,"value":8,"toc":513},"minimark",[9,13,29,44,49,72,90,123,126,175,189,217,223,273,277,286,296,301,309,316,358,361,400,407,495,498,509],[10,11,5],"h1",{"id":12},"eduwin-线程模块-api",[14,15,16],"blockquote",{},[17,18,19,20,24,25,28],"p",{},"头文件：",[21,22,23],"code",{},"eduwin_thread.h","（或通过 ",[21,26,27],{},"eduwin.h"," 统一引用）",[17,30,31,32,39,40,43],{},"封装 Win32 线程创建、互斥锁、线程安全 UI 更新。",[33,34,35,36],"strong",{},"核心亮点：",[21,37,38],{},"execute_on_main()"," 隐藏了 ",[21,41,42],{},"PostMessage"," + 隐藏窗口调度的细节，任意线程中调用即可安全更新 UI。",[45,46,48],"h2",{"id":47},"初始化-清理","初始化 \u002F 清理",[50,51,56],"pre",{"className":52,"code":53,"language":54,"meta":55,"style":55},"language-c shiki shiki-themes github-light github-dark","int  thread_init(void);\nvoid thread_cleanup(void);\n","c","",[21,57,58,66],{"__ignoreMap":55},[59,60,63],"span",{"class":61,"line":62},"line",1,[59,64,65],{},"int  thread_init(void);\n",[59,67,69],{"class":61,"line":68},2,[59,70,71],{},"void thread_cleanup(void);\n",[17,73,74,77,78,81,82,85,86,89],{},[21,75,76],{},"thread_init()"," 必须在 ",[21,79,80],{},"init_app()"," 之后调用（因为内部需要消息循环）。",[21,83,84],{},"thread_cleanup()"," 在 ",[21,87,88],{},"run_app()"," 返回后调用。",[50,91,93],{"className":52,"code":92,"language":54,"meta":55,"style":55},"init_app();\nthread_init();\n\u002F\u002F ... 创建窗口、启动线程 ...\nrun_app();\nthread_cleanup();\n",[21,94,95,100,105,111,117],{"__ignoreMap":55},[59,96,97],{"class":61,"line":62},[59,98,99],{},"init_app();\n",[59,101,102],{"class":61,"line":68},[59,103,104],{},"thread_init();\n",[59,106,108],{"class":61,"line":107},3,[59,109,110],{},"\u002F\u002F ... 创建窗口、启动线程 ...\n",[59,112,114],{"class":61,"line":113},4,[59,115,116],{},"run_app();\n",[59,118,120],{"class":61,"line":119},5,[59,121,122],{},"thread_cleanup();\n",[45,124,125],{"id":125},"线程创建",[50,127,129],{"className":52,"code":128,"language":54,"meta":55,"style":55},"typedef void (*thread_func_t)(void *arg);\ntypedef struct thread_s thread_t;\n\nthread_t* thread_run(thread_func_t func, void *arg);\nint  thread_join(thread_t *t, int timeout_ms);\nvoid thread_detach(thread_t *t);\nDWORD thread_self_id(void);\nvoid thread_sleep(int ms);\n",[21,130,131,136,141,147,152,157,163,169],{"__ignoreMap":55},[59,132,133],{"class":61,"line":62},[59,134,135],{},"typedef void (*thread_func_t)(void *arg);\n",[59,137,138],{"class":61,"line":68},[59,139,140],{},"typedef struct thread_s thread_t;\n",[59,142,143],{"class":61,"line":107},[59,144,146],{"emptyLinePlaceholder":145},true,"\n",[59,148,149],{"class":61,"line":113},[59,150,151],{},"thread_t* thread_run(thread_func_t func, void *arg);\n",[59,153,154],{"class":61,"line":119},[59,155,156],{},"int  thread_join(thread_t *t, int timeout_ms);\n",[59,158,160],{"class":61,"line":159},6,[59,161,162],{},"void thread_detach(thread_t *t);\n",[59,164,166],{"class":61,"line":165},7,[59,167,168],{},"DWORD thread_self_id(void);\n",[59,170,172],{"class":61,"line":171},8,[59,173,174],{},"void thread_sleep(int ms);\n",[17,176,177,180,181,184,185,188],{},[21,178,179],{},"thread_run"," 启动一个新线程。线程签名简化为 ",[21,182,183],{},"void func(void*)","，无需 ",[21,186,187],{},"DWORD WINAPI"," 模板代码。",[17,190,191,194,195,198,199,202,203,206,207,210,211,213,214,216],{},[21,192,193],{},"thread_join"," 的 ",[21,196,197],{},"timeout_ms","：",[21,200,201],{},"-1"," 无限等待，",[21,204,205],{},"0"," 不等待，",[21,208,209],{},">0"," 等待指定毫秒数。返回 ",[21,212,205],{}," 表示线程已结束，",[21,215,201],{}," 表示超时。",[17,218,219,222],{},[21,220,221],{},"thread_detach"," 分离线程，线程结束后自动释放资源。",[50,224,226],{"className":52,"code":225,"language":54,"meta":55,"style":55},"void my_worker(void *arg) {\n    thread_sleep(1000);\n    \u002F\u002F 做点事...\n}\n\nthread_t *t = thread_run(my_worker, NULL);\n\u002F\u002F ... 干别的 ...\nthread_join(t, 5000);  \u002F\u002F 等最多5秒\nthread_detach(t);       \u002F\u002F 释放资源\n",[21,227,228,233,238,243,248,252,257,262,267],{"__ignoreMap":55},[59,229,230],{"class":61,"line":62},[59,231,232],{},"void my_worker(void *arg) {\n",[59,234,235],{"class":61,"line":68},[59,236,237],{},"    thread_sleep(1000);\n",[59,239,240],{"class":61,"line":107},[59,241,242],{},"    \u002F\u002F 做点事...\n",[59,244,245],{"class":61,"line":113},[59,246,247],{},"}\n",[59,249,250],{"class":61,"line":119},[59,251,146],{"emptyLinePlaceholder":145},[59,253,254],{"class":61,"line":159},[59,255,256],{},"thread_t *t = thread_run(my_worker, NULL);\n",[59,258,259],{"class":61,"line":165},[59,260,261],{},"\u002F\u002F ... 干别的 ...\n",[59,263,264],{"class":61,"line":171},[59,265,266],{},"thread_join(t, 5000);  \u002F\u002F 等最多5秒\n",[59,268,270],{"class":61,"line":269},9,[59,271,272],{},"thread_detach(t);       \u002F\u002F 释放资源\n",[45,274,276],{"id":275},"线程安全-ui-更新","线程安全 UI 更新",[50,278,280],{"className":52,"code":279,"language":54,"meta":55,"style":55},"void execute_on_main(HWND hwnd, void (*cb)(void *), void *arg);\n",[21,281,282],{"__ignoreMap":55},[59,283,284],{"class":61,"line":62},[59,285,279],{},[17,287,288,291,292,295],{},[33,289,290],{},"可在任意线程中调用。"," ",[21,293,294],{},"cb(arg)"," 会在主线程的消息循环中被执行。如果调用时已经位于主线程，直接执行不走队列。",[17,297,298],{},[33,299,300],{},"工作原理：",[50,302,307],{"className":303,"code":305,"language":306},[304],"language-text","子线程 ──→ execute_on_main(hwnd, callback, arg)\n                ↓ 打包为 main_call_t\n                ↓ PostMessage\n      隐藏 dispatch 窗口 ──→ WM_DISPATCH_CALL ──→ 主线程执行 callback(arg)\n","text",[21,308,305],{"__ignoreMap":55},[17,310,311,312,315],{},"无需子类化窗口过程，无需自定义 ",[21,313,314],{},"WM_USER"," 消息号。",[50,317,319],{"className":52,"code":318,"language":54,"meta":55,"style":55},"static void on_log(void *msg) {\n    llog((const WCHAR*)msg);\n    free(msg);\n}\n\nvoid my_thread(void *arg) {\n    execute_on_main(g_win, on_log, _wcsdup(L\"线程更新了 UI\"));\n}\n",[21,320,321,326,331,336,340,344,349,354],{"__ignoreMap":55},[59,322,323],{"class":61,"line":62},[59,324,325],{},"static void on_log(void *msg) {\n",[59,327,328],{"class":61,"line":68},[59,329,330],{},"    llog((const WCHAR*)msg);\n",[59,332,333],{"class":61,"line":107},[59,334,335],{},"    free(msg);\n",[59,337,338],{"class":61,"line":113},[59,339,247],{},[59,341,342],{"class":61,"line":119},[59,343,146],{"emptyLinePlaceholder":145},[59,345,346],{"class":61,"line":159},[59,347,348],{},"void my_thread(void *arg) {\n",[59,350,351],{"class":61,"line":165},[59,352,353],{},"    execute_on_main(g_win, on_log, _wcsdup(L\"线程更新了 UI\"));\n",[59,355,356],{"class":61,"line":171},[59,357,247],{},[45,359,360],{"id":360},"互斥锁",[50,362,364],{"className":52,"code":363,"language":54,"meta":55,"style":55},"typedef struct mutex_s mutex_t;\n\nmutex_t* mutex_create(void);\nvoid mutex_lock(mutex_t *m);\nint  mutex_trylock(mutex_t *m);    \u002F\u002F 0=锁定成功\nvoid mutex_unlock(mutex_t *m);\nvoid mutex_free(mutex_t *m);\n",[21,365,366,371,375,380,385,390,395],{"__ignoreMap":55},[59,367,368],{"class":61,"line":62},[59,369,370],{},"typedef struct mutex_s mutex_t;\n",[59,372,373],{"class":61,"line":68},[59,374,146],{"emptyLinePlaceholder":145},[59,376,377],{"class":61,"line":107},[59,378,379],{},"mutex_t* mutex_create(void);\n",[59,381,382],{"class":61,"line":113},[59,383,384],{},"void mutex_lock(mutex_t *m);\n",[59,386,387],{"class":61,"line":119},[59,388,389],{},"int  mutex_trylock(mutex_t *m);    \u002F\u002F 0=锁定成功\n",[59,391,392],{"class":61,"line":159},[59,393,394],{},"void mutex_unlock(mutex_t *m);\n",[59,396,397],{"class":61,"line":165},[59,398,399],{},"void mutex_free(mutex_t *m);\n",[17,401,402,403,406],{},"基于 ",[21,404,405],{},"CRITICAL_SECTION","（用户态锁，无内核切换，比 Windows Mutex 对象轻量）。",[50,408,410],{"className":52,"code":409,"language":54,"meta":55,"style":55},"static int g_counter = 0;\nstatic mutex_t *g_mutex = NULL;\n\nvoid counter_thread(void *arg) {\n    mutex_lock(g_mutex);\n    g_counter++;\n    mutex_unlock(g_mutex);\n}\n\nint main(void) {\n    thread_init();\n    g_mutex = mutex_create();\n    \u002F\u002F ... 启动若干线程 ...\n    mutex_free(g_mutex);\n    thread_cleanup();\n}\n",[21,411,412,417,422,426,431,436,441,446,450,454,460,466,472,478,484,490],{"__ignoreMap":55},[59,413,414],{"class":61,"line":62},[59,415,416],{},"static int g_counter = 0;\n",[59,418,419],{"class":61,"line":68},[59,420,421],{},"static mutex_t *g_mutex = NULL;\n",[59,423,424],{"class":61,"line":107},[59,425,146],{"emptyLinePlaceholder":145},[59,427,428],{"class":61,"line":113},[59,429,430],{},"void counter_thread(void *arg) {\n",[59,432,433],{"class":61,"line":119},[59,434,435],{},"    mutex_lock(g_mutex);\n",[59,437,438],{"class":61,"line":159},[59,439,440],{},"    g_counter++;\n",[59,442,443],{"class":61,"line":165},[59,444,445],{},"    mutex_unlock(g_mutex);\n",[59,447,448],{"class":61,"line":171},[59,449,247],{},[59,451,452],{"class":61,"line":269},[59,453,146],{"emptyLinePlaceholder":145},[59,455,457],{"class":61,"line":456},10,[59,458,459],{},"int main(void) {\n",[59,461,463],{"class":61,"line":462},11,[59,464,465],{},"    thread_init();\n",[59,467,469],{"class":61,"line":468},12,[59,470,471],{},"    g_mutex = mutex_create();\n",[59,473,475],{"class":61,"line":474},13,[59,476,477],{},"    \u002F\u002F ... 启动若干线程 ...\n",[59,479,481],{"class":61,"line":480},14,[59,482,483],{},"    mutex_free(g_mutex);\n",[59,485,487],{"class":61,"line":486},15,[59,488,489],{},"    thread_cleanup();\n",[59,491,493],{"class":61,"line":492},16,[59,494,247],{},[45,496,497],{"id":497},"完整示例",[17,499,500,501,504,505,508],{},"参见 ",[21,502,503],{},"samples\u002Fthread_demo\u002F","（3 个并发线程 + 互斥锁计数器 + ",[21,506,507],{},"execute_on_main"," 更新 UI）。",[510,511,512],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":55,"searchDepth":68,"depth":68,"links":514},[515,516,517,518,519],{"id":47,"depth":68,"text":48},{"id":125,"depth":68,"text":125},{"id":275,"depth":68,"text":276},{"id":360,"depth":68,"text":360},{"id":497,"depth":68,"text":497},"md",{},"\u002Fdocs\u002Fapi\u002Feduwin-thread-api",{"title":5,"description":55},"docs\u002Fapi\u002Feduwin-thread-api","JJgz-EFfkyP0O1fXS4mEflIJ8_dy8veihZnbX94Qosk",1782488679849]