VC深入详解之第十五章多线程编程

本文详细解析了线程与进程的区别,深入探讨了线程的组成部分及创建过程,同时介绍了互斥对象的使用方法,以及如何通过互斥对象保证应用程序的唯一性运行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

真正完成代码执行的是线程,而进程知识线程的容器,或者说是线程的执行环境。
线程有两部分组成:线程的内核对象和线程栈


#include<windows.h>
#include<iostream>
using namespace std;


DWORD WINAPI Func1Proc(LPVOID lpParameter);
DWORD WINAPI Func2Proc(LPVOID lpParameter);


int index = 0;
int tickets = 100;
HANDLE hMutex;


void main()
{
HANDLE hThread1;
HANDLE hThread2;


hThread1 = CreateThread(NULL, 0, Func1Proc, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, Func2Proc, NULL, 0, NULL);

CloseHandle(hThread1);
CloseHandle(hThread2);


hMutex = CreateMutex(NULL, TRUE, NULL);
//WaitForSingleObject(hMutex,INFINITE);
ReleaseMutex(hMutex);
//ReleaseMutex(hMutex);
Sleep( 4000);
}


DWORD WINAPI Func1Proc( LPVOID lpParameter)
{
while(TRUE)
{
// Sleep(1);
WaitForSingleObject( hMutex, INFINITE);
if(tickets>0)
{
Sleep(1);
cout << "thread1 sell ticket : " << tickets-- << endl;
}
else
{
break;
}
ReleaseMutex(hMutex);
}
return 0;
}




DWORD WINAPI Func2Proc( LPVOID lpParameter)
{
while(TRUE)
{
//Sleep(1);
WaitForSingleObject( hMutex, INFINITE);
if(tickets>0)
{
Sleep(1);
cout << "thread2 sell ticket : " << tickets-- << endl;
}
else
{
break;
}
ReleaseMutex(hMutex);

}
return 0;
}


创建线程参数说明:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,//设置为NULL,则指定默认的安全属性,如果希望所有的子进程能够继承该线程对象的句柄,则需要设定该结构体,并将结构体中bInheritHandle成员初始化为TRUE
DWORD dwStackSize,//设置线栈初始化大小,如果值为0,那么默认将使用与调用该函数的饿线程相同的栈空间大小
LPTHREAD_START_ROUTINE lpStartAddress,//新线程的其实地址,一般为一个函数名
LPVOID lpParameter,//向新线程函数传递的参数
DWORD dwCreateionFlags,//创建标志:为CREATE_SUSPENDED就处于暂停状态,直至调用ResumeThread函数。如果值为0,就立即运行
LPDWORD lpThreadId//输出参数,接收线程的ID,可以为NULL
);


创建互斥对象的参数说明:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,//执行互斥对象的安全属性,可以为NULL
BOOL bInitialOwner,//指定互斥对象初始的拥有者
LPCTSTR lpName//指定互斥对象的名称,如果值为NULL,则创建的是一个匿名的互斥对象
);


请求和释放互斥对象:
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds);
dwMilliseconds指定等待时间的间隔,以毫秒为单位。如果将此参数设置为INFINITE,则该函数会永远等待,知道等待的对象处于有信号状态才会返回。
BOOL ReleaseMutex( HANDLE hMutex);


对于互斥对象来说,它是唯一与线程相关的内核对象。当主线程拥有互斥对象时,操作系统会将互斥对象的线程ID设置为主线程的ID。当其他线程中调用ReleaseMutex函数释放互斥对象的所有权时,操作系统会判断该线程的线程ID与互斥对象内部所维护的线程ID是否相等,只有相等才能完成释放操作。也就是说,对互斥对象来说,谁拥有谁释放。
同样,当调用WaitForSingleObject函数时,操作系统也会去判断当前请求互斥对象的线程的ID是否与互斥对象当前拥有者的线程ID相等,如果相等,即使该互斥对象处于未通知状态,调用线程任然能够获得其所有权,然后WaitForSingleObject函数返回。对于同一个线程多次拥有的互斥对象来说,该互斥对象内部的计数器记录了该线程拥有的次数,也就是说:当一个线程拥有互斥对象以后,再次调用WaitForSingleObject函数,互斥对象的计数器就增加1,当调用ReleaseMutex函数释放互斥对象的所有权时,互斥对象的计数器减1,如果该计数器的值不为0,那么该互斥对象任然处于未通知状态。




保证应用程序只有一个实例运行:
要实现只有一个实例运行的功能,可以通过命名的互斥对象来实现。在调用CreateMutex函数创建一个命名的互斥对象后,如果其返回值是一个有效的就句柄,那么可以接着调用GetLastError函数,如果该函数返回的是ERROR_ALREADY_EXISTS,这就表明先前已经创建了这个命名的互斥对象,因此就可以知道先前已经有该应用程序的一个实例在运行了。当然,如果GetLastError函数返回的不是ERROR_ALREADY_EXISTS,就说明这个互斥对象是新创建的,从而也就知道当前启动的这个进程是应用程序第一个实例。




在MFC中提供了一个完成版本协商的函数:
BOOL AfxSocketInit( WSADATA * lpwsData = NULL );//应该在应用程序类重载的InitInstance函数中调用AfxSocketInit函数。


IP控件对应的MFC类时:CIPAddressCtrl。这个类有一个GetAddress成员函数,该函数将返回IP地址控件中非空白字段的数值。




定义一个线程用以接收消息:
DWORD WINAPI CChatDlg::RecvProc(LPVOID lpParameter)
{
SOCKET sock = ((RECVPARAM*)lpParameter)->sock;
HWND hwnd = ((RECVPARAM*)lpParameter)->hwnd;
delete lpParameter;


SOCKADDR_IN addrFrom;
int len = sizeof(SOCKADDR);


char recvBuf[200];
char tempBuf[300];
int retval;
while(1)
{
retval = recvfrom(sock, recvBuf, 200, 0, (SOCKADDR*)&addrFrom, &len);
if(SOCKET_ERROR == retval)
{
break;
}
sprintf(tempBuf, "%s say: %s" , inet_ntoa(addrFrom.sin_addr), recvBuf);
::PostMessage(hwnd, WM_RECVDATA, 0, (LPARAM)tempBuf);


}
return 0;
}


网络编程补充:
在服务器端,需要用到SOCKADDR_IN结构的一个变量,供bind只用,与此同时,还需要定义一个SOCKADDR_IN变量,用于recvfrom接收时保存客户端地址。
在客户端:
需要定义一个SOCKADDR_IN变量,供sendto发送时指定地址。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值