第 9章 多线程内容摘要:
1,创建线程
2,实现线程间通信
3,利用线程同步技术保障数据安全返回目录
9.1 创建线程学习目标调用 AfxBeginThread( ) 来启动线程 。
9.1
返回第 9章程序的实现步骤
1,利用 AppWizard生成程序框架 (ThreadTest )
2,编辑资源
3,添加菜单响应函数
4,编写线程函数
5,在视中输出信息
9.1
步骤 1 生成程序框架 (MenuTest)
1,项目名称,ThreadTest
2,选择单文档界面应用程序( Single document)
9.1
步骤 2 编辑资源
1.编辑菜单资源线程 Pop-up 选中
Caption ID
启动线程 ID_THREAD_START
其他任务 ID_TREEAD_OTHER
2.编辑对话框资源
9.1
IDD_DIALOG_THRAED
CDlgThread
步骤 3 添加菜单响应函数
Class Name Objects IDs Messages
1,CThreadTestView ID_THREAD_START COMMAND
2,CThreadTestView ID_TREEAD_OTHER COMMAND
9.1
voidCThreadTestView::OnThreadStart()
{
HWND hWnd = GetSafeHwnd();
AfxBeginThread(TreadProc,hWnd,THREAD_PRIORITY_NORMAL);
}
voidCThreadTestView::OnThraedOther()
{
CDlgThread dlg;
dlg.DoModal();
}
步骤 4 编写线程函数
1,在视类中添加两个成员变量。并在构造函数中初始化
9.1
classCThreadTestView,public CView
{
……
public:
CString m_strMessage;
int m_iTime;
……
}
CThreadTestView::CThreadTestView()
{
m_strMessage = "没有线程启动 ";
m_iTime = 0;
}
步骤 5 在视中输出信息
9.1
void CThreadTestView::OnDraw(CDC* pDC)
{
CThreadTestDoc*pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO,add draw code for native data here
char chNumber[6];
itoa(m_iTime,chNumber,10);
pDC->TextOut(30,30,m_strMessage);
pDC->TextOut(30,50,chNumber);
}
2,线程函数 9.1
UINT TreadProc(LPVOID param)
{
CThreadTestApp*pApp=(CThreadTestApp *) AfxGetApp();
CMainFrame *pMainFrame = (CMainFrame *)pApp->GetMainWnd();
CThreadTestView*pView = (CThreadTestView*) pMainFrame->GetActiveView();
pView->m_strMessage= "启动了一个线程 ! ";
while(pView->m_iTime< 20)
{
::Sleep(1000);
pView->m_iTime++;
pView->Invalidate();
}
pView->m_iTime=0;
pView->m_strMessage= "线程结束 ! ";
return 0;
}
基本知识
1,CWnd类的 GetSafeHwnd();返回窗口句柄
2,AfxBeginThread( ) ;
3,线程函数 ( 返回 UINT)
4,CMainFrame::GetActiveView();
5.,:Sleep(1000);
9.1
9.2 线程间通信学习目标
1,使用全局变量实现 线程间通信
2.使用消息实现 线程间通信
3.使用 CEvent类实现 线程间通信
9.2
返回第 9章
1,使用全局变量实现线程间通信在上一节程序的基础上,进行以下操作:
( 1) 在,线程,菜单中添加菜单项:,终止线程,,ID_THREAD_STOP。
( 2) 在 ThreadTestView.cpp文件中添加一个全局变量 threadController,用来控制线程是否继续 。 添加方法是在 ThreadTestView.cpp的最上面,在
endif下面添加下面的语句:
volatile int threadController;
( 3) 在视类中为,停止线程,添加消息处理函数 OnThreadStop( ) 。
9.2
void CThreadTestView::OnThreadStop()
{
threadController = 0;
}
1,使用全局变量实现线程间通信
4,修改 OnThreadStart ( ) 函数,代码如下所示:
9.2
voidCThreadView::OnStartthread()
{
threadController= 1;
HWND hWnd = GetSafeHwnd();
AfxBeginThread(TreadProc,hWnd,THREAD_PRIORITY_NORMAL);
}
5.修改 ThreadProc()函数的代码,代码如下:
9.2
UINT TreadProc(LPVOID param)
{
CThreadTestApp*pApp=(CThreadTestApp *) AfxGetApp();
CMainFrame *pMainFrame = (CMainFrame *)pApp->GetMainWnd();
CThreadTestView *pView = (CThreadTestView *) pMainFrame->GetActiveView();
pView->m_strMessage= "启动了一个线程 ! ";
while(threadController)
{
::Sleep(1000);
pView->m_iTime ++;
pView->Invalidate();
}
pView->m_iTime =0;
pView->m_strMessage= "线程结束 ! ";
return 0;
}
2.使用消息实现线程间通信
( 1)在 ThreadTestView.h文件中定义消息:
const WM_THREAD_SENDMESS = WM_USER + 20;
( 2)在 ThreadTestView.h文件中添加消息函数声明:
afx_msg int OnThreadSendmess();
( 3) 在 ThreadTestView.cpp文件中添加消息映射:
ON_MESSAGE(WM_THREAD_SENDMESS,OnThreadSendmess)
( 4) OnThreadSendmess()代码如下:
9.2
intCThreadTestView::OnThreadSendmess()
{
m_iTime ++;
Invalidate();
return 0;
}
2.使用消息实现线程间通信
( 5)修改 ThreadProc()函数如下,
9.2
UINT TreadProc(LPVOID param)
{
CThreadTestApp *pApp=(CThreadTestApp *) AfxGetApp();
CMainFrame *pMainFrame = (CMainFrame *)pApp->GetMainWnd();
CThreadTestView*pView = (CThreadTestView *) pMainFrame->GetActiveView();
pView->m_strMessage = "启动了一个线程 ! ";
while(threadController)
{
::Sleep(1000);
::PostMessage((HWND)param,WM_THREAD_SENDMESS,0,0);
}
pView->m_iTime =0;
pView->m_strMessage = "线程结束 ! ";
return 0;
}
基本知识
1,PostMessage((HWND)param,WM_THREAD_SENDMESS,0,0);
4.5
3.使用 CEvent类实现线程间通信
( 1) Event对象:有两种状态:通信状态和非通信状态
( 2)创建一个 CEvent类的对象很,CEvent threadStart;
他自动的处在未通信状态
( 3) threadStart.SetEvent(); 使其处于通信状态
( 4)调用 WaitForSingleObject() 来监视 CEvent对象的:
::WaitForSingleObject(threadStart.m_hObject,INFINITE);
9.2
3.使用 CEvent类实现线程间通信在上一节程序的基础上,进行以下操作:
( 1)在 ThreadTestView.cpp中,#include "afxmt.h"
( 2)在 ThreadTestView.cpp中加上下列两个全局变量:
CEvent threadStart;
CEvent threadEnd;
( 3) 修改 ThreadProc( ) 函数
( 4) 修改 OnThreadStart( ) 函数中的内容 。
( 5) 修改 OnThreadStop( ) 函数中的内容 。
( 6) CThreadTestView添加 WM_CREATE消息处理函数 OnCreate( ) 。
9.2
voidCThreadTestView::OnThreadStart()
{
threadStart.SetEvent();
}
voidCThreadTestView::OnThreadStop()
{
threadEnd.SetEvent();
}
3.使用 CEvent类实现线程间通信 9.2UINT TreadProc(LPVOID param){
CThreadTestApp *pApp=(CThreadTestApp *) AfxGetApp();
CMainFrame *pMainFrame = (CMainFrame *)pApp->GetMainWnd();
CThreadTestView*pView = (CThreadTestView *) pMainFrame->GetActiveView();
::WaitForSingleObject(threadStart.m_hObject,INFINITE);
pView->m_strMessage = "启动了一个线程 ! ";
BOOL keepRunning = TRUE;
while(keepRunning)
{
::Sleep(1000);
intresult =,:WaitForSingleObject(threadEnd.m_hObject,0);
if (result == WAIT_OBJECT_0)
keepRunning = FALSE;
::PostMessage((HWND)param,WM_THREAD_SENDMESS,0,0);
}
pView->m_iTime =0;
pView->m_strMessage = "线程结束 ! ";
return 0;
3.使用 CEvent类实现线程间通信 9.2
intCThreadTestView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO,Add your specialized creation code here
HWND hWnd = GetSafeHwnd();
AfxBeginThread(TreadProc,hWnd,THREAD_PRIORITY_NORMAL);
return 0;
}
9.3 线程同步学习目标
1,使用 Critical Section实现 线程同步
2,使用互斥对象( mutex)实现 线程同步
3,使用信号量( semaphore实现 线程同步
9.3
返回第 9章
9.3.1 使用 Critical Section
1,CCriticalSection criticalSection;
2,criticalSection.Lock(); //访问前
3,criticalSection.Unlock(); //访问后
9.3
9.3.1 使用 Critical Section
一个 使用 Critical Section 的线程安全类。
9.3
#include"afxmt.h"
class CIntArray
{
private:
intarray[10];
CCriticalSection criticalSection;
public:
CIntArray();
~CIntArray() ;
voidSetArray(int value);
voidGetArray(int dstArray[10]);
};
9.3.1 使用 Critical Section
一个 使用 Critical Section 的线程安全类。
9.3
#include"stdafx.h"
#include"IntArray.h"
CIntArray::CIntArray()
{
}
CIntArray::~CIntArray()
{
}
voidCIntArray::SetArray(int value)
{
criticalSection.Lock();
for (int x=0; x<10; ++x)
array[x]= value;
criticalSection.Unlock();
}
voidCIntArray::GetArray(int dstArray[10])
{
criticalSection.Lock();
for (int x=0; x<10; ++x)
dstArray[x]= array[x];
criticalSection.Unlock();
}
9.3.1 使用 Critical Section
一个使用 CIntArray的例子。
9.3
( 1) 使用应用向导创建一个单文档应用程序 ( ThreadTest ) 。
( 2) 将 CIntArray类的头文件和实现文件中添加到项目中 。
( 3) 编辑菜单资源
,线程,菜单
ID_THREAD_START,,启动线程,。
( 4) 在视类中添加该菜单项的响应函数
OnThreadStart( ) 。
9.3.1 使用 Critical Section 9.3
( 5) 在 ThreadTestView.cpp文件中,加入下面 6行语句 。
#include "MainFrm.h"
#include "afxmt.h"
#include "IntArray.h"
CIntArray intArray;
int iArray1[10];
nt intSetNumber;
( 6) 添加下面两个全程函数 。
UINT WriteThreadProc(LPVOID param)
UINT ReadThreadProc(LPVOID param)
( 7) 在 OnDraw()中添加代码
9.3.1 使用 Critical Section 9.3
UINT WriteThreadProc(LPVOID param)
{
CThreadTestApp*pApp=(CThreadTestApp *) AfxGetApp();
CMainFrame *pMainFrame = (CMainFrame *)pApp->GetMainWnd();
CThreadTestView*pView = (CThreadTestView *) pMainFrame->GetActiveView();
for(int i=1; i<=10; i++)
{
intArray.SetArray(i);
intSetNumber = i;
pView->Invalidate();
::Sleep(2000);
}
return 0;
}
9.3.1 使用 Critical Section 9.3
UINT ReadThreadProc(LPVOID param)
{
CThreadTestApp*pApp=(CThreadTestApp *) AfxGetApp();
CMainFrame *pMainFrame = (CMainFrame *)pApp->GetMainWnd();
CThreadTestView*pView = (CThreadTestView *) pMainFrame->GetActiveView();
while(1)
{
intArray.GetArray(iArray1);
pView->Invalidate();
}
return 0;
}
9.3.1 使用 Critical Section 9.3
voidCThreadView::OnStartthread()
{
// TODO,Add your command handler code here
HWND hWnd = GetSafeHwnd();
AfxBeginThread(WriteThreadProc,hWnd);
AfxBeginThread(ReadThreadProc,hWnd);
}
9.3.1 使用 Critical Section 9.3
voidCThreadTestView::OnDraw(CDC* pDC)
{
charstr[70];
strcpy(str,"线程 1 为数组设置的值是,");
intlen = strlen(str);
wsprintf(&str[len],"%d ",intSetNumber);
pDC->TextOut(30,30,str);
strcpy(str,"线程 2 读到的值是,");
for (int i=0; i<10; ++i)
{
intlen = strlen(str);
wsprintf(&str[len],"%d ",iArray1[i]);
}
pDC->TextOut(30,50,str);
}
9.3.2 使用使用 Mutex(互斥对象)
将上例中的 critical section替换成 Mutex
9.3
#include "afxmt.h"
class CIntArray
{
private:
intarray[10];
CMutex mutex;
public:
CIntArray ();
~ CIntArray ();
voidSetArray(int value);
voidGetArray(int dstArray[10]);
};
9.3.2 使用使用 Mutex(互斥对象) 9.3
#include "stdafx.h"
#include "CIntArray.h"
CIntArray::CIntArray()
{
}
CIntArray::~CIntArray()
{
}
voidCIntArray::SetArray(int value)
{
CSingleLock singleLock(&mutex);
singleLock.Lock();
for (int x=0; x<10; ++x)
array[x]= value;
}
voidCIntArray::GetArray(int dstArray[10])
{
CSingleLock singleLock(&mutex);
singleLock.Lock();
for (int x=0; x<10; ++x)
dstArray[x]= array[x];
}
9.3.3 使用信号量( Semaphore)
( 1) 创建信号量
CSemaphore Semaphore(2,2);
( 2) 创建一个 CSingleLock对象
CSingleLock singleLock(semaphore);
( 3) 减小信号量计数器
singleLock.Lock();
( 4) 增加信号量计数器
singleLock.Unlock ();
9.3
使用信号量的一个例子
1,修改 CIntArray类
9.3
#include"afxmt.h"
class CIntArray
{
private:
intarray[10];
CMutex mutex;
CSemaphore *semaphore;
public:
CIntArray ();
~ CIntArray ();
voidSetArray(int value);
voidGetArray(int dstArray[10]);
};
使用信号量的一个例子
1,修改 CIntArray类
9.3
CIntArray::CIntArray()
{
semaphore = new CSemaphore(2,2);
}
CIntArray::~CIntArray()
{
delete semaphore;
}
voidCIntArray::SetArray(int value)
{
CSingleLocksingleLock(&mutex);
singleLock.Lock();
for (int x=0; x<10; ++x)
array[x]= value;
}
void CIntArray::GetArray(int dstArray[10])
{
CSingleLocksingleLock(semaphore);
singleLock.Lock();
for (int x=0; x<10; ++x)
dstArray[x]= array[x];
Sleep(2000);
}
使用信号量的一个例子
2,修改 CthreadTestView.cpp文件的开始部分
9.3
#include"MainFrm.h"
#include"afxmt.h"
#include"IntArray.h"
CIntArray intArray;
int iArray1[10];
int iArray2[10];
int iArray3[10];
int iArray4[10];
int iArray5[10];
int iArray6[10];
int iArray7[10];
intintSetNumber;
使用信号量的一个例子
3,改写视类中的 OnThreadStart( ) 函数
9.3
voidCThreadTestView::OnThreadStart()
{
// TODO,Add your command handler code here
HWND hWnd = GetSafeHwnd();
AfxBeginThread(WriteThreadProc,hWnd);
AfxBeginThread(ReadThreadProc1,hWnd);
AfxBeginThread(ReadThreadProc2,hWnd);
AfxBeginThread(ReadThreadProc3,hWnd);
AfxBeginThread(ReadThreadProc4,hWnd);
AfxBeginThread(ReadThreadProc5,hWnd);
AfxBeginThread(ReadThreadProc6,hWnd);
AfxBeginThread(ReadThreadProc7,hWnd);
}
使用信号量的一个例子
4,添加另外 6个线程函数
9.3
UINT ReadThreadProc2(LPVOID param)
{
CThreadTestApp *pApp=(CThreadTestApp *) AfxGetApp();
CMainFrame *pMainFrame = (CMainFrame *)pApp->GetMainWnd();
CThreadTestView*pView = (CThreadTestView *) pMainFrame->GetActiveView();
while(1)
{
intArray.GetArray(iArray2);
pView->Invalidate();
}
return 0;
}
使用信号量的一个例子
9.3
UINT ReadThreadProc3(LPVOID param)
{
CThreadTestApp *pApp=(CThreadTestApp *) AfxGetApp();
CMainFrame *pMainFrame = (CMainFrame *)pApp->GetMainWnd();
CThreadTestView*pView = (CThreadTestView *) pMainFrame->GetActiveView();
while(1)
{
intArray.GetArray(iArray3);
pView->Invalidate();
}
return 0;
}
…………,.
…………,.
5,改写视类的 OnDraw( ) 函数使用信号量的一个例子
9.3voidCThreadTestView::OnDraw(CDC* pDC)
{
CThreadTestDoc*pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO,add draw code for native data here
charstr[70];
strcpy(str,"写线程为数组设置的值是,");
intlen = strlen(str);
wsprintf(&str[len],"%d ",intSetNumber);
pDC->TextOut(30,30,str);
strcpy(str,"读线程 1 读到的值是,");
for (int i=0; i<10; ++i)
{
intlen = strlen(str);
wsprintf(&str[len],"%d ",iArray1[i]);
}
pDC->TextOut(30,50,str); 返回第 9章