MFC多线程处理:CEvent

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
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西笑生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值