《TCP/IP網絡編程》學習筆記 | Chapter 19:Windows 平臺下線程的使用

《TCP/IP網絡編程》學習筆記 | Chapter 19:Windows 平臺下線程的使用

  • 《TCP/IP網絡編程》學習筆記 | Chapter 19:Windows 平臺下線程的使用
    • 內核對象
      • 內核對象的定義
      • 內核對象歸操作系統所有
    • 基于 Windows 的線程創建
      • 進程與線程的關系
      • Windows 中線程的創建方法
      • Windows 線程的銷毀時間點
      • 編寫多線程程序的環境設置
      • 創建“使用線程安全標準 C 函數”的線程
      • 句柄、內核對象和 ID 間的關系
    • 內核對象的 2 種狀態
      • 內核對象狀態及狀態查看
      • 驗證內核對象狀態的 2 個函數
    • 習題
      • (1)下列關于內核對象的說法錯誤的是?
      • (2)現代操作系統大部分都在操作系統級別支持線程。根據該情況判斷下列描述中錯誤的是?
      • (3)請比較從內存中完全銷毀 Windows 線程和 Linux 線程的方法。
      • (4)通過線程創建過程解釋內核對象、線程、句柄之間的關系。
      • (5)判斷下列關于內核對象描述的正誤。
      • (6)請解釋“auto-reset模式”和manual-reset模式”的內核對象。區分二者的內核對象特征是什么?

《TCP/IP網絡編程》學習筆記 | Chapter 19:Windows 平臺下線程的使用

內核對象

要想掌握 Windows 平臺下的線程,應首先理解內核對象(Kernel Objects)的概念。

內核對象的定義

操作系統創建的資源有很多種,如進程、線程、文件及即將介紹的信號量、互斥量等。其中大部分都是通過程序員的請求創建的,而且請求方式各不相同。雖然存在一些差異,但它們之間也有如下共同點:都是由 Windows 操作系統創建并管理的資源。

不同資源類型在管理方式也有差異。例如,文件管理中應注冊并更新文件相關的數據I/O位置、文件的打開模式(rcad or write)等。如果是線程,則應注冊并維護線程ID、線程所屬進程等信息。操作系統為了以記錄相關信息的方式管理各種資源,在其內部生成數據塊(結構體變量)。當然,每種資源需要維護的信息不同,所以每種資源擁有的數據塊格式也有差異。這類數據塊稱為“內核對象”。

假設在 Windows 下創建了 mydata.txt 文件,此時 Windows 操作系統將生成 1 個數據塊以便管理,該數據塊就是內核對象。同理,Windows 在創建進程、線程、線程同步信號量時也會生成相應的內核對象,用于管理操作系統資源。

內核對象歸操作系統所有

線程、文件等資源的創建請求均在進程內部完成,因此,很容易產生“此時創建的內核對象所有者就是進程”的錯覺。其實,內核對象所有者是內核(操作系統),內核對象的創建、管理、銷毀時機的決定等工作均由操作系統完成。

基于 Windows 的線程創建

進程與線程的關系

現代操作系統都支持線程,因此,非顯式創建線程的程序可描述為“單一線程模型的應用程序”,顯式創建單獨線程的程序可描述為“多線程模型的應用程序”。這就意味著 main 函數的運行同樣基于線程完成,此時進程可以比喻為裝有線程的籃子,實際的運行主體是線程。

Windows 中線程的創建方法

#include <windows.h>HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,SIZE_T dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId
);

參數:

  • IpThreadAttributes:線程安全相關信息,使用默認設置時傳遞 NULL。
  • dwStackSize:要分配給線程的棧大小,傳遞 0 時生成默認大小的棧。
  • IpStartAddress:傳遞線程的 main 函數信息。
  • lpParameter:調用 main 函數時傳遞的參數信息。
  • dwCreationFlags:用于指定線程創建后的行為,傳遞 0 時,線程創建后立即進入可執行狀態。
  • IpThreadld:用于保存線程 ID 的變量地址值。

成功時返回線程句柄,失敗時返回 NULL。

調用該函數將創建線程,操作系統為了管理這些資源也將同時創建內核對象。最后返回用于區分內核對象的整數型“句柄”(Handle)。

第 1 章已介紹過,句柄相當于 Linux的文件描述符。

上述定義看起來有些復雜,其實只需要考慮 IpStartAddress 和 lpParameter 這 2 個參數,剩下的只需傳遞 0 或 NULL 即可。

Windows 線程的銷毀時間點

Windows 線程在首次調用的線程 main 函數返回時銷毀(銷毀時間點和銷毀方法與 Linux 不同)。

還有其他方法可以終止線程,但最好的方法就是讓線程main函數終止(返回)。

編寫多線程程序的環境設置

舊的 VC++6.0版默認只包含支持單線程的庫,需要自行配置。

在項目的屬性-代碼生成界面,可以檢查運行庫:

在這里插入圖片描述

創建“使用線程安全標準 C 函數”的線程

通過 CreateThread 函數調用創建出的線程在使用 C/C++ 標準函數時并不穩定。

如果線程要調用 C/C++ 標準函數,需要通過如下方法創建線程:

#include <process.h>unitptr_t _beginthreadex(void *security,unsigned stack_size,unsigned (*start_address)(void *),void *arglist,unsigned initflag,unsigned *thrdaddr
);

上述函數與之前的 CreateThread 函數相比,參數個數及各參數的含義和順序的相同,只是變量名和參數類型有所不同。因此,用上述函數替換 CreateThread 函數時,只需適當更改數據類型。上述函數的返回值類型 uintptr_t 是 64 位 unsigned 整數型。

程序示例:

在這里插入代碼片

運行結果:

在這里插入圖片描述

與 Linux 相同,Windows 同樣在 main 函數返回后終止進程,也同時終止其中包含的所有線程。另外,如果對上述代碼進行運行的話,最后輸出的內容并非字符串"end of main",而是"running thread"。但這是在 main 函數返回后,完全銷毀進程前輸出的字符串。

句柄、內核對象和 ID 間的關系

線程屬于操作系統管理資源,伴隨內核對象的創建,為了引用內核對象而返回句柄。

可以通過句柄區分內核對象,通過內核對象可以區分線程,最終,線程句柄成為區分線程的工具。

句柄的整數值在不同進程中可能重復。通過 CreateThread/_beginthreadex 函數可以得到線程 ID,它用于區分操作系統創建的所有線程,在跨進程范圍內不會重復。

內核對象的 2 種狀態

應用程序實現過程中需要特別關注的信息被賦予某種“狀態”。

線程終止狀態又稱 signaled 狀態,未終止狀態稱為 non-signaled 狀態。

內核對象狀態及狀態查看

進程或線程終止時,操作系統會把相應的內核對象改為 signaled 狀態。

這也意味著,進程和線程的內核對象初始狀態是 non-signaled 狀態。

內核對象帶有 1 個 boolean 變量,其初始值為 FALSE,此時的狀態就是 non-signaled 狀態。如果發生約定的情況,把該變量改為 TRUE,此時的狀態就是 signaled 狀態。內核對象類型不同,進入 signaled 狀態的情況也有所區別(即對應事件也有區別)。

驗證內核對象狀態的 2 個函數

首先介紹 WaitForSingleObject 函數,該函數針對單個內核對象驗證 signaled 狀態。

#include<windows.h>DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds
);

參數:

  • hHandle:查看狀態的內核對象句柄。
  • dwMilliseconds:以 1ms 為單位指定超時,傳遞 INFINITE 時函數不會返回,直到內核對象變成 signaled狀態。

進入 signaled 狀態返回 WAIT_OBJECT_0,超時返回 WAIT_TIMEOUT,失敗時返回 WAIT_FAILED。

該函數由于發生事件(變為 signaled 狀態)返回時,有時會把相應內核對象再次改為 non-signaled 狀態。這種可以再次進入 non-signaled 狀態的內核對象稱為“auto-reset模式”的內核對象,而不會自動跳轉到 non-signaled 狀態的內核對象稱為“manual-reset模式”的內核對象。

WaitForMultipleObjects 函數與 WaitForSingleObject 函數不同,可以驗證多個內核對象狀態。

#include <windows.h>DWORD WaitForMultipleObjects(DWORD nCount,const HANDLE *lphHandles,BOOL bWaitAll,DWORD dwMilliseconds
);

參數:

  • nCount:需驗證的內核對象數。
  • IpHandles:存有內核對象句柄的數組地址值。
  • bWaitAll:如果為 TRUE,則所有內核對象全部變為 signaled 時返回;如果為 FALSE,則只要有 1 個對象的狀態變為 signaled 就會返回。
  • dwMilliseconds:以 1ms 為單指定超時,傳遞 INFINITE 時函數不會返回,直到內核對象變為 signaled 狀態。

成功時返回事件信息,失敗時返回 WAIT_FAILED。

下面利用 WaitForSingleObject 函數嘗試解決示例的問題。

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>unsigned WINAPI ThreadFunc(void *arg); // WINAPI 是 Windows 固有的關鍵字,表示遵守 _beginthreadex 函數的調用規定int main(int argc, char *argv[])
{HANDLE hThread;DWORD wr;unsigned threadID;int param = 5;hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void *)&param, 0, &threadID);if (hThread == 0){puts("_beginthreadex() error");return -1;}if ((wr = WaitForSingleObject(hThread, INFINITE)) == WAIT_FAILED){puts("thread wait error");return -1;}printf("wait result: %s \n", (wr == WAIT_OBJECT_0) ? "signaled" : "time-out");puts("end of main");system("pause");return 0;
}unsigned WINAPI ThreadFunc(void *arg)
{int cnt = *((int *)arg);for (int i = 0; i < cnt; i++){Sleep(1000);puts("runnning thread");}return 0;
}

運行結果:

在這里插入圖片描述

可以看出,thread1_win.c 中的問題得到解決。

第 18 章在 Linux 平臺下分析了臨界區問題,本章最后的內容將留給 Windows 平臺下的臨界區問題。

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>#define NUM_THREAD 50
unsigned WINAPI threadInc(void *arg);
unsigned WINAPI threadDes(void *arg);
long long num = 0;int main(int argc, char *argv[])
{HANDLE tHandles[NUM_THREAD];int i;printf("sizeof long long: %d \n", sizeof(long long));for (i = 0; i < NUM_THREAD; i++){if (i % 2)tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadInc, NULL, 0, NULL);elsetHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadDes, NULL, 0, NULL);}WaitForMultipleObjects(NUM_THREAD, tHandles, TRUE, INFINITE);printf("result: %lld \n", num);system("pause");return 0;
}unsigned WINAPI threadInc(void *arg)
{int i;for (i = 0; i < 50000000; i++)num += 1;return 0;
}unsigned WINAPI threadDes(void *arg)
{int i;for (i = 0; i < 50000000; i++)num -= 1;return 0;
}

運行結果:

在這里插入圖片描述

即使多運行幾次也無法得到正確結果,而且每次結果都不同。可以利用第 20 章的同步技術得到預想的結果。

習題

(1)下列關于內核對象的說法錯誤的是?

a. 內核對象是操作系統保存各種資源信息的數據塊。
b. 內核對象的所有者是創建該內核對象的進程。
c. 由用戶進程創建并管理內核對象。
d. 無論操作系統創建和管理的資源類型是什么,內核對象的數據塊結構都完全相同。

答:

b、c、d。

(2)現代操作系統大部分都在操作系統級別支持線程。根據該情況判斷下列描述中錯誤的是?

a. 調用 main 函數的也是線程。
b. 如果進程不創建線程,則進程內不存在任何線程。
c. 多線程模型是進程內可以創建額外線程的程序類型。
d. 單一線程模型是進程內只額外創建 1 個線程的程序模型。

答:

b、d。

(3)請比較從內存中完全銷毀 Windows 線程和 Linux 線程的方法。

在這里插入圖片描述

(4)通過線程創建過程解釋內核對象、線程、句柄之間的關系。

線程創建過程:

  1. 用戶程序通過系統調用請求創建線程。
  2. 內核創建線程對象。
  3. 內核將線程對象的引用封裝為句柄,返回給用戶程序。
  4. 線程開始執行用戶定義的 main 函數,代碼運行在用戶態。
  5. 當線程函數結束且所有句柄關閉,內核銷毀線程對象。

在這里插入圖片描述

總結:

內核對象是操作系統的核心資源管理者,線程是用戶程序與內核協作的執行單元,句柄是用戶程序安全訪問內核對象的橋梁。三者通過線程創建過程緊密協作,確保資源的隔離性、安全性和高效管理。

(5)判斷下列關于內核對象描述的正誤。

  • 內核對象只有 signaled 和 non- signaled 這 2 種狀態。(×)
  • 內核對象需要轉為 signaled 狀態時,需要程序員親自將內核對象的狀態改為 signaled 狀態。(×)
  • 線程的內核對象在線程運行時處于 sigaled 狀態,線程終止則進入 non-signaled 狀態。(×)

(6)請解釋“auto-reset模式”和manual-reset模式”的內核對象。區分二者的內核對象特征是什么?

auto-reset 模式:當事件被觸發,只有一個等待線程會被喚醒,隨后事件自動重置為 non-signaled 狀態。如果有多個線程在等待,只有一個線程能繼續執行,其余線程繼續等待。

manual-reset 模式:當事件被觸發,所有等待線程都會被喚醒,事件保持 signaled 狀態,直到手動重置為 non- signaled 狀態。

選擇哪種模式取決于具體需求:是喚醒單個線程還是多個線程。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/72792.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/72792.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/72792.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

分布式事務解決方案:Seata原理詳解與實戰教程

一、為什么需要Seata&#xff1f; 在微服務架構中&#xff0c;跨服務的事務管理成為核心痛點&#xff1a; 傳統事務失效&#xff1a;服務拆分導致無法使用本地事務數據不一致風險&#xff1a;網絡抖動、服務宕機等情況導致數據錯亂復雜場景處理難&#xff1a;涉及多個數據庫、…

docker需要sudo才能使用

一種方法是添加當前用戶到docker組里去&#xff0c;當時添加的時候貌似是沒問題的&#xff0c;但是現在又不可以了 產生的報錯 ? docker images Cannot connect to the Docker daemon at unix:///home/ying/.docker/desktop/docker.sock. Is the docker daemon running?解決…

學習記錄 6 pointnet復現

一、復現代碼 然后去找相關的2d的聲吶圖像分類的算法 融合可以搞的&#xff0c;雖然有文獻但是不多&#xff0c;感覺也是可以的 """ Author: Benny Date: Nov 2019 """import os import sys import torch import numpy as npimport datetime …

Linux 文件操作-標準IO函數3- fread讀取、fwrite寫入、 fprintf向文件寫入格式化數據、fscanf逐行讀取格式化數據的驗證

目錄 1. fread 從文件中讀取數據 1.1 讀取次數 每次讀取字節數 < 原內容字節數 1.2 讀取次數 每次讀取字節數 > 原內容字節數 2.fwrite 向文件中寫入數據 2.1寫入字符串驗證 2.2寫入結構體驗證 3. fprintf 將數據寫入到指定文件 4. fscanf 從文件中逐行讀取內容…

Python 中下劃線 “_” 的多面性:從變量到約定

# Python中下劃線“_”的多面性&#xff1a;從變量到約定 在Python的語法體系里&#xff0c;下劃線“_”看似毫不起眼&#xff0c;實則扮演著極為重要且多樣化的角色。它不僅能作為普通變量參與編程&#xff0c;更在多個特殊場景下有著獨特的用途與約定。深入理解下劃線的各種…

深入 Linux 聲卡驅動開發:核心問題與實戰解析

1. 字符設備驅動如何為聲卡提供操作接口&#xff1f; 問題背景 在 Linux 系統中&#xff0c;聲卡被抽象為字符設備。如何通過代碼讓應用程序能夠訪問聲卡的錄音和播放功能&#xff1f; 核心答案 1.1 字符設備驅動的核心結構 Linux 字符設備驅動通過 file_operations 結構體定…

基于Spring Boot的圖書管理系統的設計與實現(LW+源碼+講解)

專注于大學生項目實戰開發,講解,畢業答疑輔導&#xff0c;歡迎高校老師/同行前輩交流合作?。 技術范圍&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容&#xff1a;…

記錄 macOS 上使用 Homebrew 安裝的軟件

Homebrew 是 macOS 上最受歡迎的軟件包管理器之一&#xff0c;能夠輕松安裝各種命令行工具和 GUI 應用。本文記錄了我通過 Homebrew 安裝的各種軟件&#xff0c;并對它們的用途和基本使用方法進行介紹。 &#x1f37a; Homebrew 介紹 Homebrew 是一個開源的包管理器&#xff…

個人AI助手的未來:Yi AI開源系統助力快速搭建

摘要 Yi AI推出了一站式個人AI助手平臺解決方案&#xff0c;助力用戶快速搭建專屬AI助手。該平臺采用全套開源系統&#xff0c;涵蓋前端應用、后臺管理及小程序功能&#xff0c;并基于MIT協議開放使用。同時&#xff0c;平臺集成了本地RAG方案&#xff0c;利用Milvus與Weaviate…

dpkg-architecture命令詳解

dpkg-architecture 是 Debian 系系統中用于處理軟件包架構相關操作的工具&#xff0c;尤其在軟件包構建和交叉編譯環境中至關重要。以下是其核心功能及用法的詳細說明&#xff1a; ?一、核心功能? ?架構查詢與驗證? 顯示或驗證當前系統&#xff08;DEB_HOST_ARCH&#xff…

STM32HAL庫,解決串口UART中斷接收到的第一個字節數據丟失

1.問題描述&#xff1a; 只有上電后第一次接收到的第一字節數據會丟失&#xff0c;往后再接收也不會存在問題了。 2.先貼出來重寫UART中斷回調函數 我在接收到第一字節數據后開啟定時器中斷的&#xff0c;做一個超時處理&#xff0c;每次接收到數據會對定時器計數值清零&…

解決 vxe-table 固定列對不齊,滾動后錯位、展開行遮擋的問題,解決渲染空白、邊框丟失問題

解決 vxe-table 固定列對不齊&#xff0c;滾動后錯位、展開行遮擋的問題&#xff0c;解決渲染空白、邊框丟失問題。 解決方法&#xff0c;更新到最新版本就完美解決了&#xff1b;v4 版本更新到 v4.12&#xff0c;v3版本更新到 v3.14 查看官網&#xff1a;https://vxetable.cn…

React相關面試題

以下是150道React面試題及其詳細回答&#xff0c;涵蓋了React的基礎知識、組件、狀態管理、路由、性能優化等多個方面&#xff0c;每道題目都盡量詳細且簡單易懂&#xff1a; React基礎概念類 1. 什么是React&#xff1f; React是一個用于構建用戶界面的JavaScript庫&#xff…

設備健康管理系統是什么,設備健康管理系統多少錢?

想象一下&#xff0c;你的汽車在儀表盤報警前 3 天&#xff0c;手機就收到 “發動機軸承剩余壽命 1500 公里” 的提醒 —— 這就是 ** 設備健康管理系統&#xff08;EHM&#xff09;** 的日常。在制造業&#xff0c;設備故障每年造成全球 3.4 萬億美元損失&#xff0c;而 80% 的…

Redis項目:秒殺業務

/*** 搶購秒殺券** param voucherId* return*/TransactionalOverridepublic Result seckillVoucher(Long voucherId) {// 1、查詢秒殺券SeckillVoucher voucher seckillVoucherService.getById(voucherId);// 2、判斷秒殺券是否合法if (voucher.getBeginTime().isAfter(LocalD…

區塊鏈(Blockchain)

區塊鏈&#xff08;Blockchain&#xff09;是一種去中心化、分布式的賬本技術&#xff0c;它通過密碼學保證數據的安全性和不可篡改性。它的核心特點包括去中心化、不可篡改性、可追溯性、智能合約等。 區塊鏈的關鍵概念 區塊&#xff08;Block&#xff09;&#xff1a;每個區…

和鯨科技受邀赴中國氣象局氣象干部培訓學院湖南分院開展 DeepSeek 趨勢下的人工智能技術應用專題培訓

為深入貫徹落實國家關于人工智能與氣象業務深度融合的戰略部署&#xff0c;提升在實際業務中應用人工智能技術解決問題的能力&#xff0c;推動氣象現代化高質量發展&#xff0c;中國氣象局氣象干部培訓學院湖南分院于 2025 年 3 月 14 日組織開展 “DeepSeek 等人工智能技術在氣…

Ubuntu 24 常用命令方法

文章目錄 環境說明1、賬號管理1.1、啟用 root 2、包管理工具 apt & dpkg2.1、apt 簡介 & 阿里源配置2.2、dpkg 簡介2.3、apt 和 dpkg 兩者之間的關系2.4、常用命令 3、啟用 ssh 服務4、防火墻5、開啟遠程登錄6、關閉交換分區7、build-essential&#xff08;編譯和開發軟…

OpenCV計算攝影學(22)將輸入的彩色圖像轉換為兩種風格的鉛筆素描效果函數pencilSketch()

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 鉛筆風格非寫實線描圖。 該函數通過圖像處理技術將輸入的彩色圖像轉換為兩種風格的鉛筆素描效果&#xff1a; dst1&#xff1a;炭筆效果的灰度圖…

hackmyvm-Smol

信息收集 ┌──(root?kali)-[/home/kali] └─# arp-scan -I eth1 192.168.56.0/24 Interface: eth1, type: EN10MB, MAC: 00:0c:29:34:da:f5, IPv4: 192.168.56.103 WARNING: Cannot open MAC/Vendor file ieee-oui.txt: Permission denied WARNING: Cannot open MAC/Vendo…