Java与Win32多线程

/*********************************************************************************************/

// Java 多线程实现

java实现多线程有2种方法:1扩展java.lang.Thread类;2实现java.lang.Runnable接口

1、扩展Thread类

public class Hello extends Thread
{
public Hello(){}
public Hello(String name)
{
this.name = name;
}
public run()
{
for(int i = 0; i < 5; ++i)
System.out.println(name + “ Run ” + i);
}

// main
public static void main(String[] args)
{
Hello h1 = new Hello("A");
Hello h2 = new Hello("B");
h1.start();
h2.start();
}
}

2、实现Runable接口

public class Hello implements Runable
{
public Hello(){}
public Hello(String name)
{
this.name = name;
}
public run()
{
for(int i = 0; i < 5; ++i)
System.out.println(Thread.currentThread().getName() + “ Run ” + i);
}

// main
public static void main(String[] args)
{
Hello h1 = new Hello("A");
Thread demo1 = new Thread(h1, "h1");
Hello h2 = new Hello("B");
Thread demo2 = new Thread(h2, "h2");
demo1.start();
demo2.start();
}
}

比较: Runable比Thread具有优势
1) 适合多个相同的程序代码的线程去处理同一个资源// 在Thread中需要用synchronized来同步
2) 可以避免java中单继承的限制
3) 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

/***********************************************************************************************************/

MFC中有两类线程: 工作者线程和用户界面线程。

二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环。

  工作者线程没有消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等。

用户界面线程一般用于处理独立于其他线程执行之外的用户输入,响应用户及系统所产生的事件和消息等。

但对于Win32的API编程而言,这两种线程是没有区别的,它们都只需线程的启动地址即可启动线程来执行任务。

  在MFC中,一般用全局函数AfxBeginThread()来创建并初始化一个线程的运行,该函数有两种重载形式,分别用于创建工作者线程和用户界面线程。

两种重载函数原型和参数分别说明如下:

(1) CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,

LPVOID pParam,

nPriority=THREAD_PRIORITY_NORMAL,

UINT nStackSize=0,

DWORD dwCreateFlags=0,

LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

PfnThreadProc:指向工作者线程的执行函数的指针,线程函数原型必须声明如下: UINT ExecutingFunction(LPVOID pParam);

请注意,ExecutingFunction()应返回一个UINT类型的值,用以指明该函数结束的原因。一般情况下,返回0表明执行成功。

pParam:传递给线程函数的一个32位参数,执行函数将用某种方式解释该值。它可以是数值,或是指向一个结构的指针,甚至可以被忽略;

nPriority:线程的优先级。如果为0,则线程与其父线程具有相同的优先级;

nStackSize:线程为自己分配堆栈的大小,其单位为字节。如果nStackSize被设为0,则线程的堆栈被设置成与父线程堆栈相同大小;

dwCreateFlags:如果为0,则线程在创建后立刻开始执行。如果为CREATE_SUSPEND,则线程在创建后立刻被挂起;

lpSecurityAttrs:线程的安全属性指针,一般为NULL;

(2) CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass,

int nPriority=THREAD_PRIORITY_NORMAL,

UINT nStackSize=0,

DWORD dwCreateFlags=0,

LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

pThreadClass 是指向 CWinThread的一个导出类的运行时类对象的指针,该导出类定义了被创建的用户界面线程的启动、退出等;其它参数的意义同形式1。

使用函数的这个原型生成的线程也有消息机制,在以后的例子中我们将发现同主线程的机制几乎一样。

下面我们对CWinThread类的数据成员及常用函数进行简要说明。

m_hThread:当前线程的句柄;

m_nThreadID:当前线程的ID;

m_pMainWnd:指向应用程序主窗口的指针

BOOL CWinThread::CreateThread(DWORD dwCreateFlags=0,

UINT nStackSize=0,

LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

该函数中的dwCreateFlags、nStackSize、lpSecurityAttrs参数和API函数CreateThread中的对应参数有相同含义,该函数执行成功,返回非0值,否则返回0。

一般情况下,调用AfxBeginThread()来一次性地创建并启动一个线程,但是也可以通过两步法来创建线程:首先创建CWinThread类的一个

对象,然后调用该对象的成员函数CreateThread()来启动该线程。

virtual BOOL CWinThread::InitInstance();

  重载该函数以控制用户界面线程实例的初始化。初始化成功则返回非0值,否则返回0。用户界面线程经常重载该函数,工作者线程一般不使用InitInstance()。

virtual int CWinThread::ExitInstance();

  在线程终结前重载该函数进行一些必要的清理工作。该函数返回线程的退出码,0表示执行成功,非0值用来标识各种错误。同

InitInstance()成员函数一样,该函数也只适用于用户界面线程。

MFC中的所有线程都是由CWinThread对象表示。而进程是采用CreateProcess创建。

(1) 调用AfxBeginThread一次创建一个进程,该函数将为你创建CWinThread对象。

    创建线程:

   When calling the function, use this sentence:   m_pThread = AfxBeginThread(ThreadFunc, &lpParam);
首先CREATE_SUSPENDED让他挂起,然后m_bAutoDelete=FALSE,接着才ResumeThread,最后还要delete那个指针
// --关键:在线程中可以接触到的信息就是lpParam了,因此这个类很重要,是线程与线程间的通信数据链。

结束线程:

m_pThread->SuspendThread();
m_pThread->ExitInstance();
TerminateThread(m_pThread->m_hThread, 0);
delete m_pThread;
m_pThread = NULL;

(2) 创建一个进程的另一种方法:

首先创建CWinThread类的一个对象,然后调用该对象的成员函数CreateThread()来启动该线程。
virtual BOOL CWinThread::InitInstance();
重载该函数以控制用户界面线程实例的初始化。初始化成功则返回非0值,否则返回0。用户界面线程经常重载该函数,工作者线程一般不
使用InitInstance()。
virtual int CWinThread::ExitInstance();
在线程终结前重载该函数进行一些必要的清理工作。该函数返回线程的退出码,0表示执行成功,非0值用来标识各种错误。同
InitInstance()成员函数一样,该函数也只适用于用户界面线程。

注意:

如果用MFC编程,不要用CreateThread,如果只是使用Runtime Library,用_BegingThread

CreateThread是由操作系统提供的接口,而AfxBeginThread和_BeginThread则是编译器对它的封装。

一般不要使用ExitThread,TerminateThread 结束一个线程;是一个比较危险的动作。

线程自己有个ThreadProc函数,在线程执行完函数体后,就能结束执行。另外,主线程结束,子线程也就结束。

(3) 多线程之间的关系:
// 等待多个线程
// ===================================================
CWinThread* pThread1 = AfxBeginThread(...);
CWinThread* pThread2 = AfxBeginThread(...);
HANDLE Handles[2];
Handles[0]=pThread1->m_hThread;
Handles[1]=pThread2->m_hThread;
WaitForMultipleObjects(2,Handles,TRUE,1000); 
// WaitForSingleObject(handle, INFINITE);// wait one thread
// ====================================================

附录线程系列函数:

Win32 提供了一系列的API函数来完成线程的创建、挂起、恢复、终结以及通信等工作。下面将选取其中的一些重要函数进行说明。

1、HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,

DWORD dwStackSize,

LPTHREAD_START_ROUTINE lpStartAddress,

LPVOID lpParameter,

DWORD dwCreationFlags,

LPDWORD lpThreadId);

该函数在其调用进程的进程空间里创建一个新的线程,并返回已建线程的句柄,其中各参数说明如下:

lpThreadAttributes:指向一个 SECURITY_ATTRIBUTES 结构的指针,该结构决定了线程的安全属性,一般置为 NULL;

dwStackSize:指定了线程的堆栈深度,一般都设置为0;

lpStartAddress:表示新线程开始执行时代码所在函数的地址,即线程的起始地址。一般情况为(LPTHREAD_START_ROUTINE)ThreadFunc,

ThreadFunc是线程函数名;

lpParameter:指定了线程执行时传送给线程的32位参数,即线程函数的参数;

dwCreationFlags:控制线程创建的附加标志,可以取两种值。如果该参数为0,线程在被创建后就会立即开始执行;如果该参数为CREATE_SUSPENDED,则系统产生线程后,该线程处于挂起状态,并不马上执行,直至函数ResumeThread被调用;

lpThreadId:该参数返回所创建线程的ID;

如果创建成功则返回线程的句柄,否则返回NULL。

2、DWORD SuspendThread(HANDLE hThread);

该函数用于挂起指定的线程,如果函数执行成功,则线程的执行被终止。 3、DWORD ResumeThread(HANDLE hThread);

该函数用于结束线程的挂起状态,执行线程。

4、VOID ExitThread(DWORD dwExitCode);

该函数用于线程终结自身的执行,主要在线程的执行函数中被调用。其中参数dwExitCode用来设置线程的退出码。

5、BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);

一般情况下,线程运行结束之后,线程函数正常返回,但是应用程序可以调用TerminateThread强行终止某一线程的执行。各参数含义如下:

hThread:将被终结的线程的句柄;

dwExitCode:用于指定线程的退出码。

使用TerminateThread()终止某个线程的执行是不安全的,可能会引起系统不稳定;虽然该函数立即终止线程的执行,但并不释放线程所占用的资源。因此,一般不建议使用该函数。

6、BOOL PostThreadMessage(DWORD idThread,

UINT Msg,

WPARAM wParam,

LPARAM lParam);

该函数将一条消息放入到指定线程的消息队列中,并且不等到消息被该线程处理时便返回。

idThread:将接收消息的线程的ID;

Msg:指定用来发送的消息;

wParam:同消息有关的字参数;

lParam:同消息有关的长参数;

调用该函数时,如果即将接收消息的线程没有创建消息循环,则该函数执行失败。

(4) 多线程同步

1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。 
2、互斥量:为协调共同对一个共享资源的单独访问而设计的。 
3、信号量:为控制一个具有有限数量用户资源而设计。 
4、事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。
//==========================================================
我使用最多的是互斥对象,因此解释下互斥对象的使用
    互斥(Mutex)是一种用途非常广泛的内核对象,能够保证多个线程对同一共享资源的互斥访问。
    只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。
    以互斥内核对象来保持线程同步可能用到的函数主要有CreateMutex()、ReleaseMutex()、WaitForSingleObject()、WaitForMultipleObjects()等。

//互斥对象句柄
HANDLE hMutex = NULL;
char g_cArray[10];
UINT ThreadProcA(LPVOID pParam)
{
//等待互斥对象通知
WaitForSingleObject(hMutex, INFINITE);
for(int i = 0; i < 10; i++)
{
g_cArray[i] = 'a';
Sleep(1);
}
//释放互斥对象
ReleaseMutex(hMutex);
return 0;
}
UINT ThreadProcB(LPVOID pParam)
{
//等待互斥对象通知
WaitForSingleObject(hEvent, INFINITE);
for(int i = 0; i < 10; i++)
{
g_cArray[10-i-1] = 'b';
Sleep(1);
}
//释放互斥对象
ReleaseMutex(hMutex);
return 0;
}
...
void CSampleDialog::OnThreadStart()
{
//创建互斥对象
hMutex = CreateMutex(NULL, FALSE, NULL);

AfxBeginThread(ThreadProcA, NULL);
AfxBeginThread(ThreadProcB, NULL);
Sleep(300);

CString sResult = CString(g_cArray);
AfxMessageBox(sResult);
}

    互斥对象在Mfc中通过CMutex类进行表述。
    使用CMutex类的方法非常简单,在构造CMutex类对象的同时可以指明待查询的互斥对象的名字,在构造函数返回后,即可访问此互斥变量。
    CMutex的父类也是CSyncObject,当然也是用Lock()和UnLock()成员函数来访问或释放CMutex对象。

//Mfc互斥类对象
CMutex g_clsMutex(FALSE, NULL);
char g_cArray[10];
UINT ThreadProcA(LPVOID pParam)
{
//等待互斥对象通知
g_clsMutex.Lock();
for(int i = 0; i < 10; i++)
{
g_cArray[i] = 'a';
Sleep(1);
}
//释放互斥对象
g_clsMutex.Unlock();
return 0;
}
UINT ThreadProcB(LPVOID pParam)
{
//等待互斥对象通知
g_clsMutex.Lock();
for(int i = 0; i < 10; i++)
{
g_cArray[10-i-1] = 'b';
Sleep(1);
}
//释放互斥对象
g_clsMutex.Unlock();
return 0;
}
...
void CSampleDialog::OnThreadStart()
{
AfxBeginThread(ThreadProcA, NULL);
AfxBeginThread(ThreadProcB, NULL);
Sleep(300);
CString sResult = CString(g_cArray);
AfxMessageBox(sResult);
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值