泰州网站制作方案,焦作河南网站建设,北京市门户网站,什么是网络营销网络营销的方法有哪些文章目录 创建线程方法一_beginthreadex函数讲解使用示例#xff1a; 方法二CreateThread函数讲解:使用示例: 互斥对象:创建互斥对象CreateMutex 互斥事件介绍创建或打开一个未命名的互斥事件对象 信号量介绍信号量的相关函数使用示例 关键段相关函数错误使用示例正确使用示例… 文章目录 创建线程方法一_beginthreadex函数讲解使用示例 方法二CreateThread函数讲解:使用示例: 互斥对象:创建互斥对象CreateMutex 互斥事件介绍创建或打开一个未命名的互斥事件对象 信号量介绍信号量的相关函数使用示例 关键段相关函数错误使用示例正确使用示例 综合demo多线程群聊服务器带客户端服务端代码客户端代码效果展示 创建线程
window创建线程的方法有两种
方法一_beginthreadex
函数讲解
所在头文件
#includeprocess.h函数原型
uintptr_t _beginthreadex( void *security,//指向 SECURITY_ATTRIBUTES 结构的指针此结构确定返回的句柄是否由子进程继承。 如果 Security 为 NULL则无法继承句柄。unsigned stack_size,//新线程的堆栈大小或 0。unsigned ( __clrcall *start_address )( void * ),//启动开始执行新线程的例程的地址。也就是线程函数的地址void *arglist,/要传递到新线程的参数列表或 NULL。unsigned initflag,//控制新线程的初始状态的标志。 将 initflag 设置为 0 以立即运行或设置为 CREATE_SUSPENDED 以在挂起状态下创建线程unsigned *thrdaddr//指向接收线程标识符的 32 位变量。 如果是 NULL则不使用它。
);线程函数示例
unsigned WINAPI 函数名称(void*arg)
{
//参数不能掉.........return 0;
}//获取创建的线程的句柄
HANDLE threadHandle(HANDLE)_beginthreadex(.....);//_beginthreadex()创建的线程会自动关闭线程句柄使用示例
#includewindows.h
#includestdlib.h
#includestdio.h
#includeprocess.h
unsigned WINAPI thread_01(void*arg)
{int cnt *((int*)arg);for (int i 0; i cnt; i){printf_s(thread_01\n);Sleep(1000);}return 0;
}unsigned WINAPI thread_02(void* arg)
{int cnt *((int*)arg);for (int i 0; i cnt; i){printf_s(thread_02\n);Sleep(1000);}return 0;}unsigned WINAPI thread_03(void* arg)
{int cnt *((int*)arg);for (int i 0; i cnt; i){printf_s(thread_03\n);Sleep(1000);}return 0;}int main()
{int a 10, b 7, c 4;HANDLE thread1(HANDLE)_beginthreadex(NULL,0,thread_01,(void*)a,0,NULL);HANDLE thread2(HANDLE)_beginthreadex(NULL, 0, thread_02, (void*)b, 0, NULL);HANDLE thread3(HANDLE)_beginthreadex(NULL, 0, thread_03, (void*)c, 0, NULL);WaitForSingleObject(thread1);//主线程等待thread1执行完 WaitForSingleObject(thread2);WaitForSingleObject(thread3);//这里要注意WaitForSingleObject(thread1);并不会阻塞 WaitForSingleObject(thread2);//WaitForSingleObject(thread3);这两句的执行system(pause);return 0;
}演示效果
方法二CreateThread
函数讲解:
//函数原型
HANDLE CreateThread([in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,//指向 SECURITY_ATTRIBUTES 结构的指针该结构确定是否可由子进程继承返回的句柄。 如果 lpThreadAttributes 为 NULL则无法继承句柄。[in] SIZE_T dwStackSize,//堆栈的初始大小以字节为单位。 系统将此值舍入到最近的页面。 如果此参数为零则新线程将使用可执行文件的默认大小。 [in] LPTHREAD_START_ROUTINE lpStartAddress,//指向要由线程执行的应用程序定义函数的指针。 此指针表示线程的起始地址。[in, optional] __drv_aliasesMem LPVOID lpParameter,//指向要传递给线程的变量的指针。[in] DWORD dwCreationFlags,//控制线程创建的标志。[out, optional] LPDWORD lpThreadId//指向接收线程标识符的变量的指针。 如果此参数为 NULL则不返回线程标识符。
);
//参数dwCreationFlags的可能取值
使用示例:
#includewindows.h
#includestdlib.h
#includestdio.h
#includeprocess.h
unsigned WINAPI thread_01(void*arg)
{int cnt *((int*)arg);for (int i 0; i cnt; i){printf_s(thread_01\n);Sleep(1000);}return 0;
}unsigned WINAPI thread_02(void* arg)
{int cnt *((int*)arg);for (int i 0; i cnt; i){printf_s(thread_02\n);Sleep(1000);}return 0;}unsigned WINAPI thread_03(void* arg)
{int cnt *((int*)arg);for (int i 0; i cnt; i){printf_s(thread_03\n);Sleep(1000);}return 0;}DWORD WINAPI thread_04 (LPVOID p)
{int cnt *((int*)p);for (int i 0; i cnt; i){printf_s(我是线程%d\n, GetCurrentThreadId());Sleep(1000);}return 0;
}int main()
{int a 10, b 7, c 4,d8;//方法一HANDLE thread1(HANDLE)_beginthreadex(NULL,0,thread_01,(void*)a,0,NULL);HANDLE thread2(HANDLE)_beginthreadex(NULL, 0, thread_02, (void*)b, 0, NULL);HANDLE thread3(HANDLE)_beginthreadex(NULL, 0, thread_03, (void*)c, 0, NULL);/*当使用 _beginthreadex 创建线程时它返回一个线程句柄HANDLE该句柄可以用于对线程进行操作和控制。这个句柄是由 _beginthreadex 内部创建并分配的。在线程运行结束后系统会自动关闭线程句柄并释放与之相关的资源。*///方法二HANDLE thread4 CreateThread(NULL,0,thread_04,(void*)d,0,NULL);CloseHandle(thread4);//关闭线程的句柄释放相关的操作系统资源WaitForSingleObject(thread1INFINITE);//主线程等待thread1执行完 WaitForSingleObject(thread2,INFINITE);WaitForSingleObject(thread3,INFINITE);WaitForSingleObject(thread4,INFINITE);system(pause);return 0;
}演示效果
互斥对象:
ps:你也可以理解为互斥锁
创建互斥对象CreateMutex
HANDLE CreateMutex([in, optional] LPSECURITY_ATTRIBUTES lpMutexAttributes,//指向 SECURITY_ATTRIBUTES 结构的指针。 如果此参数为 NULL则子进程无法继承句柄。[in] BOOL bInitialOwner,//如果此值为 TRUE 并且调用方创建了互斥体则调用线程获取互斥对象的初始所有权。 否则调用线程不会获得互斥体的所有权。 若要确定调用方是否创建了互斥体请参阅返回值部分。[in, optional] LPCWSTR lpName//互斥对象的名称。 名称限制为 MAX_PATH 个字符。 名称比较区分大小写。
);使用示例
#includewindows.h
#includestdio.h
#includestdlib.h
#includeprocess.h
int num 50000;
HANDLE hMutex;
unsigned WINAPI thread_01(void* arg)
{WaitForSingleObject(hMutex,INFINITE);//阻塞直到拿到锁for (int i 0; i 5000; i){--num;}ReleaseMutex(hMutex);//释放锁return 0;
}unsigned WINAPI thread_02(void* arg)
{WaitForSingleObject(hMutex, INFINITE);for (int i 0; i 5000; i){num;}return 0;
}
int main()
{HANDLE tHandles[20];hMutex CreateMutex(NULL, FALSE, NULL);//创建互斥对象for (int i 0; i 20; i){if (i % 2 0)tHandles[i] (HANDLE)_beginthreadex(NULL, 0, thread_01, NULL, 0, NULL);elsetHandles[i] (HANDLE)_beginthreadex(NULL, 0, thread_02, NULL, 0, NULL);}WaitForMultipleObjects(20, tHandles, TRUE, INFINITE);CloseHandle(hMutex);printf(num%d\n, num);//50000system(pause);return 0;
}效果演示
互斥事件
介绍
Windows中的互斥事件对象是一种同步对象用于多个线程之间的互斥访问共享资源。它被用作一种机制确保在任意时刻只有一个线程能够访问被保护的资源。 互斥事件对象有两个状态有信号signaled和无信号nonsignaled。当互斥事件对象处于有信号状态时表示资源可供访问其他线程可以继续执行当互斥事件对象处于无信号状态时表示资源正在被其他线程使用当前线程需要等待。
以下是使用互斥事件对象的一般流程 1、创建互斥事件对象使用CreateEvent函数创建一个互斥事件对象并指定其初始状态。 2、等待互斥事件对象使用WaitForSingleObject或WaitForMultipleObjects函数等待互斥事件对象。如果互斥事件对象处于无信号状态当前线程会被阻塞直到互斥事件对象变为有信号状态。 3、访问共享资源互斥事件对象变为有信号状态后当前线程可以访问共享资源。在访问完毕后需要释放互斥事件对象。 4、释放互斥事件对象使用SetEvent函数将互斥事件对象设为有信号状态以允许其他线程访问共享资源。如果没有其他线程在等待互斥事件对象它将保持有信号状态。
互斥事件对象的使用可以有效避免多个线程同时访问共享资源而导致的数据竞争和不一致性问题。
创建或打开一个未命名的互斥事件对象
HANDLE CreateEventA([in, optional] LPSECURITY_ATTRIBUTES lpEventAttributes,//指向 SECURITY_ATTRIBUTES 结构的指针。 如果此参数为 NULL则子进程无法继承句柄。 安全描述符[in] BOOL bManualReset,//如果此参数为 TRUE则函数将创建手动重置事件对象该对象需要使用 ResetEvent 函数将事件状态设置为非签名。 如果此参数为 FALSE则函数将创建一个自动重置事件对象在释放单个等待线程后系统会自动将事件状态重置为未签名。[in] BOOL bInitialState,//如果此参数为 TRUE则会向事件对象发出初始状态信号;否则它将不进行签名。[in, optional] LPCSTR lpName//事件对象的名称。 名称限制为 MAX_PATH 个字符。 名称比较区分大小写。
);/*
如果函数成功则返回值是事件对象的句柄。
如果命名事件对象在函数调用之前存在则函数将返回现有对象的句柄
GetLastError 将返回 ERROR_ALREADY_EXISTS。
如果函数失败则返回值为 NULL。 要获得更多的错误信息请调用 GetLastError。
*/SetEvent(Handle event);//将事件对象event设置为有信号状态这相当于还没线程拿到这把锁
ResetEvent(Handle event);//将事件对象重置为无信号状态信号量
介绍
触发状态(有信号状态)表示有可用资源。 未触发状态无信号状态表示没有可用资源
信号量的组成 ①计数器该内核对象被使用的次数 ②最大资源数量标识信号量可以控制的最大资源数量带符号的 32 位 ③当前资源数量标识当前可用资源的数量带符号的 32 位。即表示当前 开放资源的个数注意不是剩下资源的个数只有开放的资源才能被线程所申 请。但这些开放的资源不一定被线程占用完。比如当前开放 5 个资源而只有 3 个线程申请则还有 2 个资源可被申请但如果这时总共是 7 个线程要使用信 号量显然开放的资源 5 个是不够的。这时还可以再开放 2 个直到达到最大资 源数量。
信号量的相关函数
//创建信号量
HANDLE WINAPI CreateSemaphoreW(
_In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // Null 安全属性
_In_ LONG lInitialCount, //初始化时共有多少个资源是可以用的。 0未触发状//态无信号
状态表示没有可用资源
_In_ LONG lMaximumCount, //能够处理的最大的资源数量 3
_In_opt_ LPCWSTR lpName //NULL 信号量的名称
);//增加信号量WINAPI ReleaseSemaphore(
_In_ HANDLE hSemaphore, //信号量的句柄
_In_ LONG lReleaseCount, //将lReleaseCount值加到信号量的当前资源计数上面 0- 1
_Out_opt_ LPLONG lpPreviousCount //当前资源计数的原始值
);WaitForSingleObject(信号量对象的句柄, INFINITE)
当这个函数检测到信号量对象的资源计数大于0的时候会立即返回同时该信号量对象的资源计数值减一使用示例
#include stdio.h
#include windows.h
#include process.hunsigned WINAPI Read(void* arg);
unsigned WINAPI Accu(void* arg);
static HANDLE semOne;
static HANDLE semTwo;
static int num;int main(int argc, char* argv[])
{
HANDLE hThread1, hThread2;
semOne CreateSemaphore(NULL, 0, 1, NULL);
//semOne 没有可用资源 只能表示0或者1的二进制信号量 无信号
semTwo CreateSemaphore(NULL, 1, 1, NULL);
//semTwo 有可用资源有信号状态 有信号
hThread1 (HANDLE)_beginthreadex(NULL, 0, Read, NULL, 0, NULL);
hThread2 (HANDLE)_beginthreadex(NULL, 0, Accu, NULL, 0, NULL);
WaitForSingleObject(hThread1, INFINITE);
WaitForSingleObject(hThread2, INFINITE);
CloseHandle(semOne);
CloseHandle(semTwo);
system(pause);
return 0;
}unsigned WINAPI Read(void* arg)
{
int i;
for (i 0; i 5; i)
{
fputs(Input num: , stdout); // 1 5 11
printf(begin read\n); // 3 6 12
//等待内核对象semTwo的信号如果有信号继续执行如果没有信号等待
WaitForSingleObject(semTwo, INFINITE);
printf(beginning read\n); //4 10 16
scanf(%d, num);
ReleaseSemaphore(semOne, 1, NULL);
}
return 0;
}unsigned WINAPI Accu(void* arg)
{
int sum 0, i;
for (i 0; i 5; i)
{
printf(begin Accu\n); //2 9 15
//等待内核对象semOne的信号如果有信号继续执行如果没有信号等待
WaitForSingleObject(semOne, INFINITE);
printf(beginning Accu\n); //7 13
sum num;
printf(sum %d \n, sum); // 8 14
ReleaseSemaphore(semTwo, 1, NULL);
}
printf(Result: %d \n, sum);
return 0;
}关键段
这个和互斥事件互斥对象差不多
相关函数
初始化
InitializeCriticalSection(CRITICAL_SECTION *cs);进入关键段:
EnterCriticalSection(cs);离开关键段:
LeaveCriticalSection(cs);删除关键段
DeleteCriticalSection(cs);//删除临界区进入关键段之后到离开关键段之前的所有资源都被保证同一时刻只允许一个线程访问错误使用示例
#includeprocess.h
#includestdio.h
#includestdlib.h
#includewindows.hCRITICAL_SECTION cs;
static int ticketsNum 200;
unsigned WINAPI thread_01(void*arg)
{
;//开始卖票while (ticketsNum 0){//进入关键段EnterCriticalSection(cs);printf_s(窗口一卖出一张票还剩%d张票\n, --ticketsNum);//离开关键段LeaveCriticalSection(cs);}return 0;
}unsigned WINAPI thread_02(void*Arg)
{;//开始卖票while (ticketsNum 0){//进入关键段EnterCriticalSection(cs);printf_s(窗口二卖出一张票还剩%d张票\n, --ticketsNum);//离开关键段LeaveCriticalSection(cs);}return 0;
}int main()
{HANDLE thread1, thread2;InitializeCriticalSection(cs);thread1 (HANDLE)_beginthreadex(NULL, 0, thread_01, NULL, 0, NULL);thread2 (HANDLE)_beginthreadex(NULL, 0, thread_02, NULL, 0, NULL);WaitForSingleObject(thread1,INFINITE);WaitForSingleObject(thread2, INFINITE);CloseHandle(thread1);CloseHandle(thread2);DeleteCriticalSection(cs);//删除临界区printf_s(票卖完了\n);system(pause);return 0;
}错误效果演示 为什么会多卖出一张票呢因为这里对于循环条件ticketsNum 0判断的时候还没有进入关键段 所以这里两个线程可能同时判断了然后都进入了循环
正确使用示例
#includeprocess.h
#includestdio.h
#includestdlib.h
#includewindows.hCRITICAL_SECTION cs;
static int ticketsNum 200;
unsigned WINAPI thread_01(void* arg)
{;//开始卖票for (;;){//进入关键段EnterCriticalSection(cs);if (ticketsNum 0)break;printf_s(窗口一卖出一张票还剩%d张票\n, --ticketsNum);//离开关键段LeaveCriticalSection(cs);}return 0;
}unsigned WINAPI thread_02(void* Arg)
{;//开始卖票for (;;){//进入关键段EnterCriticalSection(cs);if (ticketsNum 0)break;printf_s(窗口二卖出一张票还剩%d张票\n, --ticketsNum);//离开关键段LeaveCriticalSection(cs);}return 0;
}int main()
{HANDLE thread1, thread2;InitializeCriticalSection(cs);thread1 (HANDLE)_beginthreadex(NULL, 0, thread_01, NULL, 0, NULL);thread2 (HANDLE)_beginthreadex(NULL, 0, thread_02, NULL, 0, NULL);WaitForSingleObject(thread1, INFINITE);WaitForSingleObject(thread2, INFINITE);CloseHandle(thread1);CloseHandle(thread2);DeleteCriticalSection(cs);//删除临界区printf_s(票卖完了\n);system(pause);return 0;
}
效果演示
综合demo多线程群聊服务器带客户端
服务端代码
#includestdio.h
#includewindows.h
#includeprocess.h
#pragma comment(lib,ws2_32.lib) //库文件#define MAX_CLIENT 256//最大连接数
#define MAX_BUF_SIZE 1024//发送数据最大大小
SOCKET clients[MAX_CLIENT];//客户端连接队列
int clientNum 0;//客户端连接数
HANDLE hMutex;//互斥对象void SendMsg(char* msg,int iLen)
{WaitForSingleObject(hMutex, INFINITE);//转发消息for (int i 0; i clientNum; i){send(clients[i], msg, iLen, 0);}ReleaseMutex(hMutex);
}
unsigned WINAPI HandleCln(void* arg)
{//线程处理函数SOCKET Client *((SOCKET*)arg);int iLen 0;char szMsg[MAX_BUF_SIZE] { 0 };for(;;){iLen recv(Client, szMsg, sizeof(szMsg), 0);if (iLen ! -1){SendMsg(szMsg, iLen);}else{break;}}//处理下线过程WaitForSingleObject(hMutex, INFINITE);for (int i 0; i clientNum; i){if (clients[i] Client){while (i clientNum){clients[i] clients[i 1];}clientNum--;printf_s(当前连接客户端数:%d\n,clientNum);break;}}ReleaseMutex(hMutex);return 0;
}
int main()
{/*******************初始化服务器*******************///初始化网络库WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested MAKEWORD(2, 2);err WSAStartup(wVersionRequested, wsaData);if (err ! 0){printf_s(WSAStartup errorNum % d\n, GetLastError());}if (LOBYTE(wsaData.wVersion) ! 2 || HIBYTE(wsaData.wVersion) ! 2){printf_s(LOBYTE errorNum%d\n, GetLastError());WSACleanup();}//创建服务器套接字SOCKET serverSocket socket(AF_INET, SOCK_STREAM, 0);SOCKADDR_IN addrSrv;addrSrv.sin_addr.S_un.S_addr htonl(INADDR_ANY);addrSrv.sin_family AF_INET;addrSrv.sin_port htons(6000);//绑定if (SOCKET_ERROR bind(serverSocket, (SOCKADDR*)addrSrv, sizeof(SOCKADDR))){printf_s(bind error%d\n, GetLastError());return -1;}//创建互斥对象hMutex CreateMutex(NULL, FALSE, NULL);//监听if (SOCKET_ERROR listen(serverSocket, MAX_CLIENT))//最大连接/监听数为5{printf_s(listen error%d\n, GetLastError());return -1;}SOCKADDR_IN addrClient;int len sizeof(SOCKADDR_IN);HANDLE hThread;for(;;){//接收客户端连接SOCKET sockConnect accept(serverSocket, (SOCKADDR*)addrClient, len);//放入队列WaitForSingleObject(hMutex, INFINITE);clients[clientNum] sockConnect;ReleaseMutex(hMutex);hThread (HANDLE)_beginthreadex(NULL,0,HandleCln,(void*)sockConnect,0,NULL);printf_s(connect client IP%s clientNum%d\n,inet_ntoa(addrClient.sin_addr),clientNum);}WSACleanup();return 0;
}客户端代码
#includestdio.h
#includestdlib.h
#includewindows.h
#includeprocess.h
#pragma comment(lib,ws2_32.lib) //库文件
#define MAX_SIZE 1024 //收发消息的最大长度
#define MAX_NAME_SIZE 32//昵称最大长度 HANDLE hSend;//发送线程
HANDLE hAceept;//接收线程
char nickName[MAX_NAME_SIZE];//昵称unsigned WINAPI sendMsg(void* arg)
{//发送消息给服务端SOCKET ClientSocket *((SOCKET*)arg);char msg[MAX_SIZE] { 0 };char name_msg[MAX_SIZE MAX_NAME_SIZE] { 0 };for (;;){memset(msg, 0, MAX_SIZE);//阻塞在这一句直到接收到控制台输入fgets(msg, MAX_SIZE, stdin);if (!strcmp(msg, Q\n) || !strcmp(msg, q\n)){//客户端口下线closesocket(ClientSocket);exit(0);}//将消息拼包发送给服务器sprintf(name_msg, %s %s, nickName,msg);send(ClientSocket, name_msg, strlen(name_msg), 0);}return 0;
}unsigned WINAPI acceptMsg(void* arg)
{//接收服务端的消息SOCKET ClientSocket *((SOCKET*)arg);char name_msg[MAX_SIZE MAX_NAME_SIZE] { 0 };int iLen 0;for (;;){memset(name_msg, 0, MAX_NAME_SIZE MAX_SIZE);iLen recv(ClientSocket, name_msg, sizeof(name_msg),0);if (iLen -1){return 2;}name_msg[iLen] 0;//将接收到的消息输出到控制台fputs(name_msg, stdout);}return 0;
}
int main()
{//初始化网络库WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested MAKEWORD(2, 2);err WSAStartup(wVersionRequested, wsaData);if (err ! 0){printf_s(WSAStartup errorNum % d\n, GetLastError());}if (LOBYTE(wsaData.wVersion) ! 2 || HIBYTE(wsaData.wVersion) ! 2){printf_s(LOBYTE errorNum%d\n, GetLastError());WSACleanup();}//安装电话机SOCKET ClientSocket socket(AF_INET, SOCK_STREAM, 0);if (INVALID_SOCKET ClientSocket){printf_s(socket error%d\n, GetLastError());return -1;}//配置要连接的服务器SOCKADDR_IN addrSrv;addrSrv.sin_addr.S_un.S_addr inet_addr(127.0.0.1);addrSrv.sin_family AF_INET;addrSrv.sin_port htons(6000);//连接服务器if (SOCKET_ERROR connect(ClientSocket, (SOCKADDR*)addrSrv, sizeof(SOCKADDR))){printf_s(connect error%d\n, GetLastError());return -1;}//输入昵称printf_s(请输入您的昵称:);scanf(%s, nickName);//清空输入缓冲区while (getchar() ! \n) {continue;}//开启发送接收线程hSend (HANDLE)_beginthreadex(NULL, 0, sendMsg, (void*)ClientSocket, 0, NULL);hAceept (HANDLE)_beginthreadex(NULL, 0, acceptMsg, (void*)ClientSocket, 0, NULL);//主线程等待WaitForSingleObject(hSend, INFINITE);WaitForSingleObject(hAceept, INFINITE);WSACleanup();printf_s(退出);return 0;
}效果展示