MFC多线程处理:CEvent
flyfish
示例 界面A写完之后,再写入B
#pragma once
#include "afxwin.h"
UINT WriteA(LPVOID pParam);
UINT WriteB(LPVOID pParam);
// CEventTestDlg 对话框
class CEventTestDlg : public CDialog
{
// 构造
public:
CEventTestDlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_EVENTTEST_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedButtonStart();
CEdit m_Edit_ctlA;
CEdit m_Edit_ctlB;
};
实现文件
#include "stdafx.h"
#include "EventTest.h"
#include "EventTestDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
CEvent g_eventWriteB;
CString g_strText;
UINT WriteA(LPVOID pParam)
{
CEdit *pEdit = (CEdit*)pParam;
pEdit->SetWindowText(_T(""));
CString n;
for (int i = 0; i<3; i++)
{
n.Format(L"%d", i);
g_strText = L"A:" + n;
pEdit->SetWindowText(g_strText);
Sleep(1000);
}
g_eventWriteB.SetEvent();
return 0;
}
UINT WriteB(LPVOID pParam)
{
CEdit *pEdit = (CEdit*)pParam;
pEdit->SetWindowText(_T(""));
CString n;
WaitForSingleObject(g_eventWriteB.m_hObject, INFINITE);
for (int i = 0; i<3; i++)
{
n.Format(L"%d", i);
g_strText = L"B:" + n;
pEdit->SetWindowText(g_strText);
Sleep(1000);
}
return 0;
}
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CEventTestDlg 对话框
CEventTestDlg::CEventTestDlg(CWnd* pParent /*=NULL*/)
: CDialog(IDD_EVENTTEST_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CEventTestDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDIT_A, m_Edit_ctlA);
DDX_Control(pDX, IDC_EDIT_B, m_Edit_ctlB);
}
BEGIN_MESSAGE_MAP(CEventTestDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON_START, &CEventTestDlg::OnBnClickedButtonStart)
END_MESSAGE_MAP()
// CEventTestDlg 消息处理程序
BOOL CEventTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CEventTestDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CEventTestDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CEventTestDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CEventTestDlg::OnBnClickedButtonStart()
{
// TODO: 在此添加控件通知处理程序代码
CWinThread *pWriteA = AfxBeginThread(WriteA,
&m_Edit_ctlA,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
pWriteA->ResumeThread();
CWinThread *pWriteB = AfxBeginThread(WriteB,
&m_Edit_ctlB,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
pWriteB->ResumeThread();
}
后面部分摘自MSDN
当一个线程必须知道何时执行其任务时,事件非常有用。 例如,在新数据可用时,必须通知线程将数据复制到数据存档。
通过使用CEvent对象时要通知的副本线程新数据是否可用,线程可以尽可能快地执行其任务。 CEvent对象具有两种类型︰ 手动和自动。
自动CEvent对象会自动返回为非终止 (不可用) 状态后至少一个线程被释放。
默认情况下,CEvent对象是自动的除非您传递TRUE为bManualReset在构造期间的参数。
手动CEvent对象而保留在设置的状态SetEvent或ResetEvent直到调用其他函数。
若要创建手动CEvent对象,将传递TRUE为bManualReset在构造期间的参数。
若要使用CEvent对象,请构造CEvent时需要它的对象。 指定要等待,并指定您的应用程序最初应拥有它的事件的名称。
然后,您可以访问时构造函数将返回的事件。 调用SetEvent信号 (使之可用) 事件对象,然后调用解锁完成后访问受控的资源。
一种方法来使用CEvent对象是添加类型的变量CEvent为你想要控制的类数据成员。
受控制的对象的构造,期间调用的构造函数CEvent数据成员并指定是否事件最初收到信号,以及 specifythe 对象类型的事件所需的事件
(如果它将用于跨进程边界),名称和任何安全性属性您想。
若要访问控制的资源CEvent对象以这种方式,请首先创建任一类型的变量CSingleLock或类型CMultiLock所需的资源的访问方法中。
然后,调用Lock的锁对象的方法 (例如, CMultiLock::Lock)。 此时,您的线程将访问资源、
等待释放和获得访问权限,或等待要释放的资源的资源、 超时值,并无法获得对资源的访问。 在任何情况下,所需的资源已访问以线程安全的方式。
若要释放资源,调用SetEvent信号通知事件对象,然后使用Unlock的锁对象的方法 (例如,
CMultiLock::Unlock),或让离开作用域的锁对象。标头︰ afxmt.h CEvent::CEvent 构造的已命名的或未命名CEvent对象。 CEvent(
BOOL bInitiallyOwn = FALSE,
BOOL bManualReset = FALSE,
LPCTSTR lpszName = NULL,
LPSECURITY_ATTRIBUTES lpsaAttribute = NULL);参数 bInitiallyOwn 如果TRUE的线程CMultilock或CSingleLock对象已启用。
否则,必须等待,想要访问该资源的所有线程。 bManualReset
如果TRUE,指定的事件对象是一个手动的事件,否则事件对象是自动的事件。 lpszName CEvent 对象的名称。
如果该对象将使用跨进程边界,必须提供。 如果名称与现有的事件相匹配,该构造函数将生成新CEvent引用该名称的事件的对象。
如果名称与一个现有的同步对象,它不事件相匹配,构造将会失败。 如果NULL,名称将为 null。 lpsaAttribute
事件对象的安全属性。 此结构的完整说明,请参阅SECURITY_ATTRIBUTES中Windows SDK。 备注
若要访问或释放CEvent对象,请创建CMultiLock或CSingleLock对象,并调用其锁和解锁成员函数。
若要更改的状态CEvent对象发出信号 (线程不必等待),调用SetEvent或PulseEvent。
若要设置的状态的CEvent对象传递给非终止 (线程必须等待),调用ResetEvent。
System_CAPS_ICON_important.jpg 重要事项
在创建后CEvent对象,请使用GetLastError以确保该互斥体尚不存在。
如果互斥体未存在意外,这可能表示一个恶意进程占用并可能想要出于恶意使用互斥体。
在这种情况下,建议的注重安全的过程是关闭句柄并继续像创建对象时出错。CEvent::PulseEvent 设置发出信号的事件的状态 (可用)、 释放任何正在等待的线程,并将其重置为非终止 (不可用) 自动。
BOOL PulseEvent();返回值 如果该函数成功,则非零值否则为 0。 备注
如果该事件是手动,所有等待的线程被都释放,则将事件设置为非终止,并PulseEvent返回。
如果该事件是自动的单个线程被释放,则将事件设置为非终止,并PulseEvent返回。
如果没有线程在等待,或没有线程可能会立即释放PulseEvent将的事件的状态设置为非终止,并返回。 PulseEvent将使用基础
Win32PulseEvent函数,可暂时从等待状态由内核模式下异步过程调用。 因此,PulseEvent是不现实,而且不能由新的应用程序。
有关详细信息,请参阅PulseEvent 函数。CEvent::ResetEvent 设置为事件的状态非终止之前显式设置为终止的SetEvent成员函数。 BOOL
ResetEvent();返回值 如果该函数成功,则非零值否则为 0。 备注 这将导致所有想要访问此事件,以等待的线程。 自动事件不使用此成员函数。
CEvent::SetEvent 释放任何正在等待的线程的情况下设置为终止状态,该事件的状态。 BOOL SetEvent();
返回值 非零,如果该函数成功,否则为 0。 备注 如果该事件是手动,事件仍将终止状态,直到ResetEvent调用。
多个线程可能会在这种情况下释放。 如果该事件是自动的该事件会保持已发出信号,直至单个线程被释放。 然后,系统会将事件状态设置为非终止。
如果没有线程在等待,状态将保持已发出信号,直到释放一个线程。CEvent::Unlock 释放事件对象。 BOOL Unlock();
返回值 非零,如果线程所拥有的事件对象和事件都是自动事件;否则为 0。 备注
由当前拥有一个自动事件,它将其释放后它们完成后,如果要重复使用其锁对象的线程调用此成员函数。
如果锁对象不是可重复使用,锁对象的析构函数将调用此函数。
MSDN提供的例子
// The following demonstrates trivial usage of the CEvent class.
// A CEvent object is created and passed as a parameter to another
// thread. The other thread will wait for the event to be signaled
// and then exit
UINT __cdecl MyThreadProc(LPVOID lpParameter)
{
CEvent* pEvent = (CEvent*)(lpParameter);
VERIFY(pEvent != NULL);
// Wait for the event to be signaled
::WaitForSingleObject(pEvent->m_hObject, INFINITE);
// Terminate the thread
::AfxEndThread(0, FALSE);
return 0L;
}
void CEvent_Test()
{
// Create the CEvent object that will be passed to the thread routine
CEvent* pEvent = new CEvent(FALSE, FALSE);
// Create a thread that will wait on the event
CWinThread* pThread;
pThread = ::AfxBeginThread(&MyThreadProc, pEvent, 0, 0, CREATE_SUSPENDED, NULL);
pThread->m_bAutoDelete = FALSE;
pThread->ResumeThread();
// Signal the thread to do the next work item
pEvent->SetEvent();
// Wait for the thread to consume the event and return
::WaitForSingleObject(pThread->m_hThread, INFINITE);
delete pThread;
delete pEvent;
}
另一个例子
// This example builds upon the previous one.
// A second thread is created to calculate prime numbers.
// The main thread will signal the second thread to calulate the next
// prime number in the series. The second thread signals the first
// after each number is calculated. Finally, after several iterations
// the worker thread is signaled to terminate.
class CPrimeTest
{
public:
CPrimeTest()
: m_pCalcNext(new CEvent(FALSE, FALSE))
, m_pCalcFinished(new CEvent(FALSE, FALSE))
, m_pTerminateThread(new CEvent(FALSE, FALSE))
, m_iCurrentPrime(0)
{
// Create a thread that will calculate the prime numbers
CWinThread* pThread;
pThread = ::AfxBeginThread(&PrimeCalcProc, this, 0, 0, CREATE_SUSPENDED, NULL);
pThread->m_bAutoDelete = FALSE;
pThread->ResumeThread();
// Calcuate the first 10 prime numbers in the series on the thread
for(UINT i = 0; i < 10; i++)
{
// Signal the thread to do the next work item
m_pCalcNext->SetEvent();
// Wait for the thread to complete the current task
::WaitForSingleObject(m_pCalcFinished->m_hObject, INFINITE);
// Print the result
TRACE(_T("The value of m_iCurrentPrime is: %d\n"), m_iCurrentPrime);
}
// Notify the worker thread to exit and wait for it to complete
m_pTerminateThread->SetEvent();
::WaitForSingleObject(pThread->m_hThread, INFINITE);
delete pThread;
}
~CPrimeTest()
{
delete m_pCalcNext;
delete m_pCalcFinished;
delete m_pTerminateThread;
}
private:
// Determines whether the given number is a prime number
static BOOL IsPrime(INT ThisPrime)
{
if(ThisPrime < 2)
return FALSE;
for(INT n = 2; n < ThisPrime; n++)
{
if(ThisPrime % n == 0)
return FALSE;
}
return TRUE;
}
// Calculates the next prime number in the series
static INT NextPrime(INT ThisPrime)
{
while(TRUE)
{
if(IsPrime(++ThisPrime))
{
return ThisPrime;
}
}
}
// Worker thread responsible for calculating the next prime
// number in the series
static UINT __cdecl PrimeCalcProc(LPVOID lpParameter)
{
CPrimeTest* pThis = static_cast<CPrimeTest*>(lpParameter);
VERIFY(pThis != NULL);
VERIFY(pThis->m_pCalcNext != NULL);
VERIFY(pThis->m_pCalcFinished != NULL);
VERIFY(pThis->m_pTerminateThread != NULL);
// Create a CMultiLock object to wait on the various events
// WAIT_OBJECT_0 refers to the first event in the array, WAIT_OBJECT_0+1 refers to the second
CSyncObject* pWaitObjects[] = { pThis->m_pCalcNext, pThis->m_pTerminateThread };
CMultiLock MultiLock(pWaitObjects, 2L);
while(MultiLock.Lock(INFINITE, FALSE) == WAIT_OBJECT_0)
{
// Calculate next prime
pThis->m_iCurrentPrime = NextPrime(pThis->m_iCurrentPrime);
// Notify main thread calculation is complete
pThis->m_pCalcFinished->SetEvent();
}
// Terminate the thread
::AfxEndThread(0, FALSE);
return 0L;
}
CEvent* m_pCalcNext; // notifies worker thread to calculate next prime
CEvent* m_pCalcFinished; // notifies main thread current calculation is complete
CEvent* m_pTerminateThread; // notifies worker thread to terminate
INT m_iCurrentPrime; // current calculated prime number
};