Windows核心编程学习九:利用内核对象进行线程同步

时间:2022-05-26 11:38:22

注:源码为学习《Windows核心编程》的一些尝试,非原创。若能有助于一二访客,幸甚。

1.程序框架

#include "Queue.h"
#include <tchar.h>
#include <windowsx.h>
#include <StrSafe.h>
#include <process.h>
#include "resource.h" /************************************************************************/ #define chHANDLE_DLGMSG(hWnd, message, fn) \
case (message): return (SetDlgMsgResult(hWnd, uMsg, \
HANDLE_##message((hWnd), (wParam), (lParam), (fn)))) // This macro function calls the C runtime's _beginthreadex function.
// The C runtime library doesn't want to have any reliance on Windows' data
// types such as HANDLE. This means that a Windows programmer needs to cast
// values when using _beginthreadex. Since this is terribly inconvenient,
// I created this macro to perform the casting.
typedef unsigned (__stdcall *PTHREAD_START) (void *); #define chBEGINTHREADEX(psa, cbStackSize, pfnStartAddr, \
pvParam, dwCreateFlags, pdwThreadId) \
((HANDLE)_beginthreadex( \
(void *) (psa), \
(unsigned) (cbStackSize), \
(PTHREAD_START) (pfnStartAddr), \
(void *) (pvParam), \
(unsigned) (dwCreateFlags), \
(unsigned *) (pdwThreadId))) // Sets the dialog box icons
inline void chSETDLGICONS(HWND hWnd, int idi)
{
SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)
LoadIcon((HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE), MAKEINTRESOURCE(idi)));
SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)
LoadIcon((HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE), MAKEINTRESOURCE(idi)));
}
BOOL Dlg_OnInitDialog(HWND hWnd, HWND hWndFocus, LPARAM lParam)
{
chSETDLGICONS(hWnd, IDI_QUEUE); return TRUE;
} void Dlg_OnCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify)
{
switch (id)
{
case IDCANCEL:
EndDialog(hWnd, id);
break;
}
} INT_PTR WINAPI Dlg_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
chHANDLE_DLGMSG(hWnd, WM_INITDIALOG, Dlg_OnInitDialog);
chHANDLE_DLGMSG(hWnd, WM_COMMAND, Dlg_OnCommand);
} return FALSE;
} /*************************************************************************/ int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine, int)
{
DialogBox(hInstExe, MAKEINTRESOURCE(IDD_QUEUE), NULL, Dlg_Proc); return 0;
}

Windows核心编程学习九:利用内核对象进行线程同步

2.线程安全队列的实现

/*
* File: CQueue.h
* Time: 2013-07-10
* 描述: 学习《Windows核心编程》
*/ #ifndef _CQUEUE_H_
#define _CQUEUE_H_ #include <windows.h> class CQueue
{
public:
struct ELEMENT {
int m_nThreadNum; // 线程号
int m_nRequestNum; // 请求号
};
typedef ELEMENT* PELEMENT; private:
PELEMENT m_pElements; // 队列元素数组
int m_nMaxElements; // 数组长度
HANDLE m_h[2]; // 两个内核对象,一个互斥量一个信号量
HANDLE &m_hmtxQ; // 互斥量对象的引用
HANDLE &m_hsemNumElements; // 信号量对象的引用 public:
CQueue(int nMaxElements);
~CQueue(); BOOL Append(PELEMENT pElement, DWORD dwMilliseconds);
BOOL Remove(PELEMENT pElement, DWORD dwMilliseconds);
}; #endif
/************************************************************************/

CQueue::CQueue(int nMaxElements) : m_hmtxQ(m_h[0]), m_hsemNumElements(m_h[1])
{
// 初始化结构数组
m_pElements = (PELEMENT)HeapAlloc(GetProcessHeap(), 0, sizeof(ELEMENT) * nMaxElements); // 初始化数组长度
m_nMaxElements = nMaxElements; // 创建互斥量和信号量内核对象
m_hmtxQ = CreateMutex(NULL, FALSE, NULL);
m_hsemNumElements = CreateSemaphore(NULL, 0, nMaxElements, NULL);
} CQueue::~CQueue()
{
// 清理内核对象和内存
CloseHandle(m_hsemNumElements);
CloseHandle(m_hmtxQ);
HeapFree(GetProcessHeap(), 0, m_pElements);
} BOOL CQueue::Append(CQueue::PELEMENT pElement, DWORD dwTimeout)
{
BOOL fOk = FALSE; // 等待互斥量内核对象
DWORD dw = WaitForSingleObject(m_hmtxQ, dwTimeout); // 返回WAIT_OBJECT_0表示得到了队列的独占访问权
if (dw == WAIT_OBJECT_0) {
LONG lPrevCount; // 尝试向队列添加新元素
fOk = ReleaseSemaphore(m_hsemNumElements, 1, &lPrevCount); // fOk为TRUE表示队列没满,可以添加新元素
if (fOk) {
m_pElements[lPrevCount] = *pElement;
}
else {
SetLastError(ERROR_DATABASE_FULL);
} // 释放互斥量,允许其他线程访问队列
ReleaseMutex(m_hmtxQ);
}
else {
SetLastError(ERROR_TIMEOUT);
} return fOk;
} BOOL CQueue::Remove(CQueue::PELEMENT pElement, DWORD dwTimeout)
{
// 确保对队列具有独占访问权,且要求队列中有元素可取
// 等待信号量成功的副作用使它的计数减一,所以不需要显示调用ReleaseSemaphore()
BOOL fOk = (WaitForMultipleObjects(_countof(m_h), m_h, TRUE, dwTimeout) == WAIT_OBJECT_0); if (fOk)
{
// 获取元素
*pElement = m_pElements[0]; // 取出索引为0 的元素,把数组中剩余元素向前挪一个位置
MoveMemory(&m_pElements[0], &m_pElements[1], sizeof(ELEMENT) * (m_nMaxElements - 1)); // 释放互斥量,允许其他线程访问队列
ReleaseMutex(m_hmtxQ);
}
else
{
SetLastError(ERROR_TIMEOUT);
} return fOk;
}

3.客户端线程

CQueue				g_q(10);
volatile LONG g_fShutdown = FALSE;
HWND g_hWnd; // Handles to all reader/writer threads
HANDLE g_hThreads[MAXIMUM_WAIT_OBJECTS]; // Number of reader/writer threads
int g_nNumThreads = 0; /************************************************************************/ DWORD WINAPI ClientThread(PVOID pvParam)
{
int nThreadNum = PtrToUlong(pvParam); // 线程号
HWND hWndLB = GetDlgItem(g_hWnd, IDC_CLIENT); int nRequestNum = 0;
while (1 != InterlockedCompareExchange(&g_fShutdown, 0, 0))
{
nRequestNum++; TCHAR sz[1024];
CQueue::ELEMENT e = { nThreadNum, nRequestNum }; // 尝试添加元素到队列
if (g_q.Append(&e, 200)) {
StringCchPrintf(sz, _countof(sz), TEXT("客户线程%d 添加元素%d"), nThreadNum, nRequestNum);
}
else {
StringCchPrintf(sz, _countof(sz), TEXT("客户线程%d 添加元素%d失败(%s)"), nThreadNum, nRequestNum,
(GetLastError() == ERROR_TIMEOUT) ? TEXT("超时") : TEXT("队列已满"));
} ListBox_SetCurSel(hWndLB, ListBox_AddString(hWndLB, sz));
Sleep(2500);
} return 0;
}
BOOL Dlg_OnInitDialog(HWND hWnd, HWND hWndFocus, LPARAM lParam)
{
chSETDLGICONS(hWnd, IDI_QUEUE); g_hWnd = hWnd; DWORD dwThreadID; // 创建客户端线程
for (int i = 0; i < 4; i++)
g_hThreads[g_nNumThreads++] = chBEGINTHREADEX(NULL, 0, ClientThread, (PVOID)(INT_PTR) i, 0, &dwThreadID); return TRUE;
}

Windows核心编程学习九:利用内核对象进行线程同步

4.服务器端线程

DWORD WINAPI ServerThread(PVOID pvParam)
{
int nThreadNum = PtrToUlong(pvParam);
HWND hWndLB = GetDlgItem(g_hWnd, IDC_SERVER); while (1 != InterlockedCompareExchange(&g_fShutdown, 0, 0)) {
TCHAR sz[1024];
CQueue::ELEMENT e; // 尝试移除元素
if (g_q.Remove(&e, 5000)) {
StringCchPrintf(sz, _countof(sz), TEXT("服务器端线程%d移除客户端线程%d放入的元素%d"), nThreadNum, e.m_nThreadNum, e.m_nRequestNum);
Sleep(2000 * e.m_nThreadNum);
}
else {
StringCchPrintf(sz, _countof(sz), TEXT("服务器端线程%d 没有元素可取"), nThreadNum);
} ListBox_SetCurSel(hWndLB, ListBox_AddString(hWndLB, sz));
} return 0;
}
BOOL Dlg_OnInitDialog(HWND hWnd, HWND hWndFocus, LPARAM lParam)
{
chSETDLGICONS(hWnd, IDI_QUEUE); g_hWnd = hWnd; DWORD dwThreadID; // 创建客户端线程
for (int i = 0; i < 4; i++)
g_hThreads[g_nNumThreads++] = chBEGINTHREADEX(NULL, 0, ClientThread, (PVOID)(INT_PTR) i, 0, &dwThreadID); // 创建读者线程
for (int i = 0; i < 2; i++)
g_hThreads[g_nNumThreads++] = chBEGINTHREADEX(NULL, 0, ServerThread, (PVOID)(INT_PTR) i, 0, &dwThreadID); return TRUE;
}
int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine, int)
{
DialogBox(hInstExe, MAKEINTRESOURCE(IDD_QUEUE), NULL, Dlg_Proc); InterlockedExchange(&g_fShutdown, TRUE); WaitForMultipleObjects(g_nNumThreads, g_hThreads, TRUE, INFINITE);
while (g_nNumThreads--)
CloseHandle(g_hThreads[g_nNumThreads]); return 0;
}

Windows核心编程学习九:利用内核对象进行线程同步

Windows核心编程学习九:利用内核对象进行线程同步的更多相关文章

  1. windows核心编程---第八章 使用内核对象进行线程同步

    使用内核对象进行线程同步. 前面我们介绍了用户模式下线程同步的几种方式.在用户模式下进行线程同步的最大好处就是速度非常快.因此当需要使用线程同步时用户模式下的线程同步是首选. 但是用户模式下的线程同步 ...

  2. Windows核心编程 第三章 内核对象

    第3章内核对象 在介绍Windows API的时候,首先要讲述内核对象以及它们的句柄.本章将要介绍一些比较抽象的概念,在此并不讨论某个特定内核对象的特性,相反只是介绍适用于所有内核对象的特性. 首先介 ...

  3. windows核心编程---第三章 内核对象及句柄本质

      本章讨论的是相对抽象的概念,不涉及任何具体的内核对象的细节而是讨论所有内核对象的共有特性. 首先让我们来了解一下什么是内核对象.内核对象通过API来创建,每个内核对象是一个数据结构,它对应一块内存 ...

  4. 操作系统中的进程同步与Window中利用内核对象进行线程同步的关系

    操作系统中为了解决进程间同步问题提出了用信号量机制,信号量可分为四种类型分别是互斥型信号量,记录型信号量,AND型信号量,信号量集. 互斥型信号量 互斥型信号量是资源数量为1的特殊的记录型信号量.表示 ...

  5. 《Windows核心编程系列》八谈谈用内核对象进行线程同步

    使用内核对象进行线程同步. 前面我们介绍了用户模式下线程同步的几种方式.在用户模式下进行线程同步的最大好处就是速度非常快.因此当需要使用线程同步时用户模式下的线程同步是首选. 但是用户模式下的线程同步 ...

  6. Windows核心编程:第9章 用内核对象进行线程同步

    Github https://github.com/gongluck/Windows-Core-Program.git //第9章 用内核对象进行线程同步.cpp: 定义应用程序的入口点. // #i ...

  7. C&plus;&plus;之内核对象进行线程同步

    用户模式下的线程同步机制提供了非常好的性能,但他们也的确存在一些局限性,而且不适用于许多应用程序,例如,对Interlocked系列函数只能对一个值进行操作,它们从来不会把线程切换到等待状态.我们可以 ...

  8. 第9章 用内核对象进行线程同步(2)&lowbar;可等待计时器(WaitableTimer)

    9.4 可等待的计时器内核对象——某个指定的时间或每隔一段时间触发一次 (1)创建可等待计时器:CreateWaitableTimer(使用时应把常量_WIN32_WINNT定义为0x0400) 参数 ...

  9. 第9章 用内核对象进行线程同步(4)&lowbar;死锁&lpar;DeadLock)及其他

    9.7 线程同步对象速查表 对象 何时处于未触发状态 何时处于触发状态 成功等待的副作用 进程 进程仍在运行的时候 进程终止的时(ExitProcess.TerminateProcess) 没有 线程 ...

随机推荐

  1. php单链表实现的代码

    <?php/*** 单链表*/ class Demo{private $id;public $name;public $next;public function __construct ($id ...

  2. java使用正则表达式验证IP V4、 IP V6

    package cn.outofmemory.snippets.core; import java.util.regex.Pattern; /** * A collection of utilitie ...

  3. quote&comma; quasiquote&comma; unquote和unquote-splicing

    关于符号类型 符号类型又称引用类型,在概要一文中本人介绍得非常的模糊,使很多初学者不理解.符号类型在Scheme语言中是最基础也是最重要的一种类型,这是因为Scheme语言的祖先Lisp语言的最初目的 ...

  4. perl 爬取某理财网站产品信息

    use LWP::UserAgent; use utf8; use DBI; $user="root"; $passwd="xxxxx"; $dbh=&quot ...

  5. Hadoop体系架构简介

    今天跟一个朋友在讨论hadoop体系架构,从当下流行的Hadoop+HDFS+MapReduce+Hbase+Pig+Hive+Spark+Storm开始一直讲到HDFS的底层实现,MapReduce ...

  6. Struts标签库详解【3】

    struts2标签库详解 要在jsp中使用Struts2的标志,先要指明标志的引入.通过jsp的代码的顶部加入以下的代码: <%@taglib prefix="s" uri= ...

  7. 使用GoldenGate EVENTACTIONS执行数据的实时触发和定制化

    Oracle GoldenGate不仅可以在线实时同步数据(包括增量和存量),也内置有一套事件触发流程,允许用户根据某张表某条记录的某个特殊字段值,触发相应的自定义执行流程,比如接收到某个银行账号的大 ...

  8. Shovel Sale CodeForces - 899D &lpar;数位dp&rpar;

    大意: n把铲子, 价格1,2,3,...n, 求有多少个二元组(x,y), 满足x+y末尾数字9的个数最多. 枚举最高位, 转化为从[1,n]中选出多少个二元组和为$x$, 枚举较小的数 若$n\g ...

  9. Vue2 学习笔记5

    文中例子代码请参考github watch属性的使用 考虑一个问题:想要实现 名 和 姓 两个文本框的内容改变,则全名的文本框中的值也跟着改变:(用以前的知识如何实现???) 监听data中属性的改变 ...

  10. Django之认证系统

    Django之认证系统 cookie和session 1.cookie不属于http协议范围,由于http协议无法保持状态,但实际情况,我们却又需要“保持状态”,因此cookie就是在这样一个场景下诞 ...