当前位置: 首页 > wzjs >正文

p2p网站数据分析怎么做郑州网络营销公司有哪些

p2p网站数据分析怎么做,郑州网络营销公司有哪些,青岛网站建设有限公司,安装下载app软件《TCP/IP网络编程》学习笔记 | Chapter 20:Windows 中的线程同步 《TCP/IP网络编程》学习笔记 | Chapter 20:Windows 中的线程同步用户模式和内核模式用户模式同步内核模式同步 基于 CRITICAL_SECTION 的同步内核模式的同步方法基于互斥量对象的同步基于…

《TCP/IP网络编程》学习笔记 | Chapter 20:Windows 中的线程同步

  • 《TCP/IP网络编程》学习笔记 | Chapter 20:Windows 中的线程同步
    • 用户模式和内核模式
      • 用户模式同步
      • 内核模式同步
    • 基于 CRITICAL_SECTION 的同步
    • 内核模式的同步方法
      • 基于互斥量对象的同步
      • 基于信号量对象的同步
      • 基于事件对象的同步
    • Windows 平台下实现多线程服务器端
    • 习题
      • (1)关于 Windows 操作系统的用户模式和内核模式的说法正确的是?
      • (2)判断下列关于用户模式同步和内核模式同步描述的正误。
      • (3)本章示例SyncSema_win.c 的 Read 函数中,退出临界区需要较长时间,请给出解决方案并实现。
      • (4)请将本章 SyncEvent_win.c 示例改为基于信号量的同步方式,并得出相同运行结果。

《TCP/IP网络编程》学习笔记 | Chapter 20:Windows 中的线程同步

用户模式和内核模式

Windows操作系统的运行方式是“双模式操作”(Dual-mode Operation):

  • 用户模式(User mode):运行应用程序的基本模式,禁止访问物理设备,而且会限制访问的内存区域。
  • 内核模式(Kernal mode):操作系统运行时的模式,不仅不会限制访问的内存区域,而且访问的硬件设备也不会受限。

实际上,在应用程序运行过程中,Windows操作系统不会一直停留在用户模式,而是在用户模式和内核模式之间切换。

例如,可以在Windows中创建线程。虽然创建线程的请求是由应用程序的函数调用完成,但实际创建线程的是操作系统。因此,创建线程的过程中无法避免向内核模式的转换。

定义这2种模式主要是为了提高安全性。应用程序的运行时错误会破坏操作系统及各种资源。特别是C/C++可以进行指针运算,很容易发生这类问题。例如,因为错误的指针运算覆盖了操作系统中存有重要数据的内存区域,这很可能引起操作系统崩溃。但实际上各位从未经历过这类事件,因为用户模式会保护与操作系统有关的内存区域。因此,即使遇到错误的指针运算也仅停止应用程序的运行,而不会影响操作系统。

总之,像线程这种伴随着内核对象创建的资源创建过程中,都要默认经历如下模式转换过程:用户模式→内核模式→用户模式。

从用户模式切换到内核模式是为了创建资源,从内核模式再次切换到用户模式是为了执行应用程序的剩余部分。不仅是资源的创建,与内核对象有关的所有事务都在内核模式下进行。

模式切换对系统而言其实也是一种负担,频繁的模式切换会影响性能。

用户模式同步

用户模式同步是用户模式下进行的同步,即无需操作系统的帮助而在应用程序级别进行的同步。

用户模式同步的最大优点是——速度快。无需切换到内核模式,仅考虑这一点也比经历内核模式切换的其他方法要快。而且使用方法相对简单,因此,适当运用用户模式同步并无坏处。

但因为这种同步方法不会借助操作系统的力量,其功能上存在一定局限性。稍后将介绍属于用户模式同步的、基于“CRITICAL_SECTION”的同步方法。

内核模式同步

下面给出内核模式同步的优点。

  • 比用户模式同步提供的功能更多。
  • 可以指定超时,防止产生死锁。

因为都是通过操作系统的帮助完成同步的,所以提供更多功能。特别是在内核模式同步中,可以跨越进程进行线程同步。因为内核对象并不属于某一进程,而是操作系统拥有并管理的。

与此同时,由于无法避免用户模式和内核模式之间的切换,所以性能上会受到一定影响。

基于 CRITICAL_SECTION 的同步

基于 CRITICAL_SECTION 的同步中将创建并运用“CRITICAL_SECTION对象”,但这并非内核对象。与其他同步对象相同,它是进入临界区的一把“钥匙”。因此,为了进入临界区,需要得到 CRITICAL_SECTION 对象这把“钥匙”。相反,离开时应上交 CRITICAL_SECTION 对象。

下面介绍 CRITICAL_SECTION 对象的初始化及销毁相关函数。

#include<windows.h>void InitilizerCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

参数:

  • IpCriticalSection:InitilizerCriticalSection 函数中传入需要初始化的 CRITICAL_SECTION 对象的地址值,DeleteCriticalSection 函数中传入需要解除的 CRITICAL_SECTION 对象的地址值。

上述函数的参数类型 LPCRITICAL_SECTION 是 CRITICAL_SECTION 指针类型。另外 DeleteCriticalSection 函数并不销毁CRITICAL_SECTION 对象。该函数的作用是销毁 CRITICAL_SECTION 对象相关的资源。

接下来介绍获取(拥有)及释放 CRITICAL_SECTION 对象的函数,可以简单理解为获取和释放“钥匙”的函数。

#include<windows.h>void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

参数:

  • IpCriticalSection:获取(拥有)及释放 CRITICAL_SECTION 对象的地址值。

与 Linux 部分中介绍过的互斥量类似,相信大部分人仅靠这些函数介绍也能写出示例程序。

示例程序:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>#define NUM_THREAD 50
unsigned WINAPI threadInc(void *arg);
unsigned WINAPI threadDes(void *arg);long long num = 0;
CRITICAL_SECTION cs;int main(int argc, char *argv[])
{HANDLE tHandles[NUM_THREAD];int i;InitializeCriticalSection(&cs);for (i = 0; i < NUM_THREAD; i++){if (i % 2)tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadInc, NULL, 0, NULL);elsetHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadDes, NULL, 0, NULL);}WaitForMultipleObjects(NUM_THREAD, tHandles, TRUE, INFINITE);DeleteCriticalSection(&cs);printf("result: %lld \n", num);system("pause");return 0;
}unsigned WINAPI threadInc(void *arg)
{int i;EnterCriticalSection(&cs);for (i = 0; i < 50000000; i++)num += 1;LeaveCriticalSection(&cs);return 0;
}unsigned WINAPI threadDes(void *arg)
{int i;EnterCriticalSection(&cs);for (i = 0; i < 50000000; i++)num -= 1;LeaveCriticalSection(&cs);return 0;
}

运行结果:

在这里插入图片描述

程序将整个循环纳入临界区,可以减少运行时间。

内核模式的同步方法

基于互斥量对象的同步

基于互斥量(Mutual Exclusion)对象的同步方法与基于 CRITICAL_SECTION 对象的同步方法类似,因此,互斥量对象同样可以理解为“钥匙”。

首先介绍创建互斥量对象的函数:

#include<windows.h>HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,BOOL bInitialOwner,LPCTSTR lpName
);

参数:

  • lpMutexAttributes:传递安全相关的配置信息,使用默认安全设置时可以传递 NULL。
  • blnitialOwner:如果为 TRUE,则创建出的互斥量对象属于调用该函数的线程,同时进入 non-signaled 状态;如果为 FALSE,则创建出的互斥量对象不属于任何线程,此时状态为 signaled。
  • IpName:用于命名互斥量对象。传入 NULL 创建无名的互斥量对象。

成功时返回创建的互斥量对象句柄,失败时返回 NULL。

从上述参数说明中可以看到,如果互斥量对象不属于任何拥有者,则将进入 signaled 状态。利用该特点进行同步。

另外,互斥量属于内核对象,所以通过如下函数销毁:

#include<windows.h>BOOL CloseHandle(HANDLE hObject);

参数:

  • hObject:要销毁的内核对象的句柄。

成功时返回 TRUE,失败时返回 FALSE。

上述函数是销毁内核对象的函数,所以同样可以销毁即将介绍的信号量及事件。下面介绍获取和释放互斥量的函数,但我认为只需介绍释放的函数,因为获取是通过各位熟悉的 WaitForSingleObject 函数完成的。

#include<windows.h>BOOL ReleaseMutex(HANDLE hMutex);

参数:

  • hMutex:需要释放的互斥量对象句柄。

成功时返回 TRUE,失败时返回 FALSE。

接下来分析获取和释放互斥量的过程。互斥量被某一线程获取时(拥有时)为 non-signaled 状态,释放时(未拥有时)进入 signaled 状态。因此,可以使用 WaitForSingleObject 函数验证互斥量是否已分配。该函数的调用结果有如下 2 种。

  • 调用后进入阻塞状态:互压量对象已被其他线程获取,现处于 non-signaled 状态。
  • 调用后直接返回:其他线程未占用互斥量对象,现处于 signaled 状态。

互斥量在 WaitForSingleObject 函数返回时自动进入 non-signaled 状态,因为它是第 19 章介绍过的"auto-reset"模式的内核对象。结果,WaitForSingleObject 函数为申请互斥量时调用的函数。因此,基于互斥量的临界区保护代码如下:

WaitForsingleobject(hMutex, INFINITE);
// 临界区的开始
// ......
// 临界区的结束
ReleaseMutex(hMutex);

WaitForSingleObject 函数使互斥量进入 non-signaled 状态,限制访问临界区,所以相当于临界区的门禁系统。相反,ReleaseMutex 函数使互斥量重新进入 signaled 状态,所以相当于临界区的出口。

示例程序:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>#define NUM_THREAD 50
unsigned WINAPI threadInc(void *arg);
unsigned WINAPI threadDes(void *arg);long long num = 0;
HANDLE hMutex;int main(int argc, char *argv[])
{HANDLE tHandles[NUM_THREAD];int i;hMutex = CreateMutex(NULL, FALSE, NULL);for (i = 0; i < NUM_THREAD; i++){if (i % 2)tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadInc, NULL, 0, NULL);elsetHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadDes, NULL, 0, NULL);}WaitForMultipleObjects(NUM_THREAD, tHandles, TRUE, INFINITE);CloseHandle(hMutex);printf("result: %lld \n", num);system("pause");return 0;
}unsigned WINAPI threadInc(void *arg)
{int i;WaitForSingleObject(hMutex, INFINITE);for (i = 0; i < 50000000; i++)num += 1;ReleaseMutex(hMutex);return 0;
}unsigned WINAPI threadDes(void *arg)
{int i;WaitForSingleObject(hMutex, INFINITE);for (i = 0; i < 50000000; i++)num -= 1;ReleaseMutex(hMutex);return 0;
}

运行结果:

在这里插入图片描述

基于信号量对象的同步

Windows 中基于信号量对象的同步也与 Linux 下的信号量类似,二者都是利用名为“信号量值”的整数值完成同步的,而且该值都不能小于 0。当然,Windows 的信号量值注册于内核对象。

下面介绍创建信号量对象的函数,其销毁同样是利用 CloseHandle 函数进行的。

#include <windows.h>HANDLE Createsemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,LONG lInitialCount,LONG lMaximumCount,LPCTSTR lpName
);

参数:

  • IpSemaphoreAttributes:安全配置信息,采用默认安全设置时传递 NULL。
  • lInitialCount:指定信号量的初始值,应大于 0 小于 lMaximumCount。
  • IMaximumCount:信号量的最大值。该值为 1 时,信号量变为只能表示 0 和 1 的二进制信号量。
  • lpName:用于命名信号量对象,传递 NULL 时创建无名的信号量对象。

成功时返回创建的信号量对象的句柄,失败时返回 NULL。

向 lInitialCount 参数传递 0 时,创建 non-signaled 状态的信号量对象。而向 IMaximumCount 传入 3 时,信号量最大值为 3,因此可以实现 3 个线程同时访问临界区时的同步。

可以利用“信量值为 0 时进入 non-signaled 状态,大于 0 时进入 signaled 状态”的特性进行同步。

下面介绍释放信号量对象的函数:

#include <windows.h>BOOL ReleaseSemaphore(HANDLE hSemaphore,LONG lReleaseCount,LPLONG lpPreviouscount
);

参数:

  • Semaphore:传递需要释放的信号量对象.
  • IReleaseCount:释放意味着信号量值的增加,通过该参数可以指定增加的值。超过最大值则不增加,返回 FALSE。
  • IpPreviousCount:用于保存修改之前值的变量地址,不需要时可传递 NULL。

成功时返回 TRUE,失败时返回 FALSE。

信号量对象的值大于 0 时成为 signaled 状态,为 0 时成为 non-signaled 状态。因此,调用 WaitForSingleObject 函数时,信号量大于 0 的情况才会返回,返回的同时将信量值减 1。可以通过如下程序结构保护临界区。

WaitForSingleObject(hSemaphore, INFINITE);
// 临界区的开始
// ......
// 临界区的结束
ReleaseSemaphore(hSemaphore, 1, NULL);

示例程序:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>unsigned WINAPI read(void *arg);
unsigned WINAPI accu(void *arg);static HANDLE sem_one;
static HANDLE sem_two;
static int num;int main(int argc, char const *argv[])
{HANDLE hThread1, hThread2;sem_one = CreateSemaphore(NULL, 0, 1, NULL);sem_two = CreateSemaphore(NULL, 1, 1, NULL);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(sem_one);CloseHandle(sem_two);system("pause");return 0;
}unsigned WINAPI read(void *arg)
{int i;for (i = 0; i < 5; i++){fputs("Input num: ", stdout);WaitForSingleObject(sem_two, INFINITE);scanf("%d", &num);ReleaseSemaphore(sem_one, 1, NULL);}return 0;
}unsigned WINAPI accu(void *arg)
{int sum = 0, i;for (i = 0; i < 5; i++){WaitForSingleObject(sem_one, INFINITE);sum += num;ReleaseSemaphore(sem_two, 1, NULL);}printf("Result: %d \n", sum);return 0;
}

运行结果:

在这里插入图片描述

在循环内部构件临界区,起到尽可能缩小临界区的作用,尽量提高程序性能。

基于事件对象的同步

事件同步对象与前 2 种同步方法相比有很大不同,区别就在于,该方式下创建对象时,在自动以 non-signaled 状态运行的“auto-reset”模式和与之相反的“manual-reset”模式中任选其一。而事件对象的主要特点是可以创建“manual-reset”模式的对象。

首先介绍用于创建事件对象的函数:

#include <windows.h>HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset,BOOL bInitialState,LPCTSTR lpName
);

参数:

  • IpEventAttributes:安全配置相关参塑,采用默认安全配置时传入 NULL。
  • bManualReset:传入 TRUE 时创建“manual-reset”模式的事件对象,传入 FALSE 时创建“auto-reset”模式的事件对象。
  • bInitialState:传入 TRUE 时创建 signaled 状态的事件对象,传入 FALSE 时创建 non-signaled态的事件对象。
  • IpName:用于命名事件对象。传递 NULL 时创建无名的事件对象。

成功时返回创建的事件对象句柄,失败时返回 NULL。

相信各位也发现了,上述函数中需要重点关注的是第二个参数。传人 TRUE 时创建“manual-reset”模式的事件对象,此时即使 WaitForSingleObject 函数返回也不会回到 non-signaled 状态。

因此,在这种情况下,需要通过如下 2 个函数明确更改对象状态:

#include <windows.h>BOOL ResetEvent(HANDLE hEvent); // 设置为 non-signaled 状态
BOOL SetEvent(HANDLE hEvent);   // 设置为 signaled 状态

成功时返回 TRUE,失败时返回 FALSE。

传递事件对象句柄并希望改为 non-signaled 状态时,应调用 ResetEvent 函数。如果希望改为 signaled 状态,则可以调用 SetEvent 函数。

示例程序:

在这里插入代码片

运行结果:

在这里插入图片描述

读入字符串后将事件改成 signaled 状态,等待的 2 个线程将摆脱等待状态,开始执行。最后还是把事件对象的状态改为 non-signaled 状态。

Windows 平台下实现多线程服务器端

服务器端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <process.h>#define BUF_SIZE 100
#define MAX_CLNT 256unsigned WINAPI HandleClnt(void *arg);
void SendMsg(char *msg, int len);
void ErrorHandling(char *msg);int clntCnt = 0;
SOCKET clntSocks[MAX_CLNT];
HANDLE hMutex;int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hServSock, hClntSock;SOCKADDR_IN servAdr, clntAdr;int clntAdrSz;HANDLE hThread;if (argc != 2){printf("Usage : %s <port>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHandling("WSAStartup() error!");hMutex = CreateMutex(NULL, FALSE, NULL);hServSock = socket(PF_INET, SOCK_STREAM, 0);memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_family = AF_INET;servAdr.sin_addr.s_addr = htonl(INADDR_ANY);servAdr.sin_port = htons(atoi(argv[1]));if (bind(hServSock, (SOCKADDR *)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)ErrorHandling("bind() error");if (listen(hServSock, 5) == SOCKET_ERROR)ErrorHandling("listen() error");while (1){clntAdrSz = sizeof(clntAdr);hClntSock = accept(hServSock, (SOCKADDR *)&clntAdr, &clntAdrSz);WaitForSingleObject(hMutex, INFINITE);clntSocks[clntCnt++] = hClntSock;ReleaseMutex(hMutex);hThread = (HANDLE)_beginthreadex(NULL, 0, HandleClnt, (void *)&hClntSock, 0, NULL);printf("Connected client IP: %s \n", inet_ntoa(clntAdr.sin_addr));}closesocket(hServSock);WSACleanup();return 0;
}unsigned WINAPI HandleClnt(void *arg)
{SOCKET hClntSock = *((SOCKET *)arg);int strLen = 0, i;char msg[BUF_SIZE];while ((strLen = recv(hClntSock, msg, sizeof(msg), 0)) != 0)SendMsg(msg, strLen);WaitForSingleObject(hMutex, INFINITE);for (i = 0; i < clntCnt; i++){if (hClntSock == clntSocks[i]){while (i++ < clntCnt - 1)clntSocks[i] = clntSocks[i + 1];break;}}clntCnt--;ReleaseMutex(hMutex);closesocket(hClntSock);return 0;
}void SendMsg(char *msg, int len)
{ // 发送给全部人int i;WaitForSingleObject(hMutex, INFINITE);for (i = 0; i < clntCnt; i++)send(clntSocks[i], msg, len, 0);ReleaseMutex(hMutex);
}void ErrorHandling(char *msg)
{fputs(msg, stderr);fputc('\n', stderr);exit(1);
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <process.h>#define BUF_SIZE 100
#define NAME_SIZE 20unsigned WINAPI SendMsg(void *arg);
unsigned WINAPI RecvMsg(void *arg);
void ErrorHandling(char *msg);char name[NAME_SIZE] = "[DEFAULT]";
char msg[BUF_SIZE];int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hSock;SOCKADDR_IN servAdr;HANDLE hSndThread, hRcvThread;if (argc != 4){printf("Usage: %s <IP> <port> <name>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHandling("WSAStartup() error!");sprintf(name, "[%s]", argv[3]);hSock = socket(PF_INET, SOCK_STREAM, 0);memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_family = AF_INET;servAdr.sin_addr.s_addr = inet_addr(argv[1]);servAdr.sin_port = htons(atoi(argv[2]));if (connect(hSock, (SOCKADDR *)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)ErrorHandling("connect() error");hSndThread = (HANDLE)_beginthreadex(NULL, 0, SendMsg, (void *)&hSock, 0, NULL);hRcvThread = (HANDLE)_beginthreadex(NULL, 0, RecvMsg, (void *)&hSock, 0, NULL);WaitForSingleObject(hSndThread, INFINITE);WaitForSingleObject(hRcvThread, INFINITE);closesocket(hSock);WSACleanup();return 0;
}unsigned WINAPI SendMsg(void *arg)
{SOCKET hSock = *((SOCKET *)arg);char nameMsg[NAME_SIZE + BUF_SIZE];while (1){fgets(msg, BUF_SIZE, stdin);if (!strcmp(msg, "q\n") || !strcmp(msg, "Q\n")){closesocket(hSock);exit(0);}sprintf(nameMsg, "%s %s", name, msg);send(hSock, nameMsg, strlen(nameMsg), 0);}return 0;
}unsigned WINAPI RecvMsg(void *arg)
{int hSock = *((SOCKET *)arg);char nameMsg[NAME_SIZE + BUF_SIZE];int strLen;while (1){strLen = recv(hSock, nameMsg, NAME_SIZE + BUF_SIZE - 1, 0);if (strLen == -1)return -1;nameMsg[strLen] = '\0';fputs(nameMsg, stdout);}return 0;
}void ErrorHandling(char *msg)
{fputs(msg, stderr);fputc('\n', stderr);exit(1);
}

编译:

gcc chat_server_win.c -lwsock32 -o cserv
gcc chat_client_win.c -lwsock32 -o cclnt

运行结果:

在这里插入图片描述

习题

(1)关于 Windows 操作系统的用户模式和内核模式的说法正确的是?

a. 用户模式是应用程序运行的基本模式,虽然访问的内存空间没有限制,但无法访问物理设备。
b. 应用程序运行过程中绝对不会进人内核模式。应用程序只在用户模式中运行。
c. Windows 为了有效使用内存空间,分别定义了用户模式和内核模式。
d. 应用程序运行过程中也有可能切换到内核模式。只是切换到内核模式后,进程将一直保持该状态。

答:

c。

(2)判断下列关于用户模式同步和内核模式同步描述的正误。

  • 用户模式的同步中不会切换到内核模式。即非操作系统级别的同步。( √ )
  • 内核模式的同步是由操作系统提供的功能,比用户模式同步提供更多功能。( √ )
  • 需要在用户模式和内核模式之间切换,这是内核模式同步的缺点。( √ )
  • 除特殊情况外,原则上应使用内核模式同步。用户模式同步是操作系统提供内核模式同步机制前使用的同步方法。( × )

(3)本章示例SyncSema_win.c 的 Read 函数中,退出临界区需要较长时间,请给出解决方案并实现。

用一个数组存储输入的 5 个数字,将整个循环改为临界区,减少进入/离开临界区的次数。

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>unsigned WINAPI read(void *arg);
unsigned WINAPI accu(void *arg);static HANDLE sem_one, sem_two;
static int arr[5];int main(int argc, char const *argv[])
{HANDLE hThread1, hThread2;sem_one = CreateSemaphore(NULL, 0, 1, NULL);sem_two = CreateSemaphore(NULL, 1, 1, NULL);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(sem_one);CloseHandle(sem_two);system("pause");return 0;
}unsigned WINAPI read(void *arg)
{WaitForSingleObject(sem_two, INFINITE);for (int i = 0; i < 5; i++){fputs("Input num: ", stdout);scanf("%d", &arr[i]);}ReleaseSemaphore(sem_one, 1, NULL);return 0;
}unsigned WINAPI accu(void *arg)
{int sum = 0;WaitForSingleObject(sem_one, INFINITE);for (int i = 0; i < 5; i++)sum += arr[i];ReleaseSemaphore(sem_two, 1, NULL);printf("Result: %d \n", sum);return 0;
}

运行结果:

在这里插入图片描述

(4)请将本章 SyncEvent_win.c 示例改为基于信号量的同步方式,并得出相同运行结果。

http://www.dtcms.com/wzjs/363998.html

相关文章:

  • 拉萨市建设局网站网络营销专业是学什么的
  • 在线做流程图的网站百度收录提交工具
  • 用.net做的网站网站建设及网站推广
  • 成都企业网站建设公司搜狗搜索排名优化
  • 宝宝投票网站怎么做推广教程
  • 群晖做网站服务器seo关键词快速排名介绍
  • 手机网站建设万网湖南网络优化
  • 山东莱州市建设局网站ai智能营销系统
  • 一个网站建设域名的构思长春网站建设平台
  • 谷歌浏览器对做网站有什么好处西安关键词优化排名
  • 企业网站托管电话优化大师免费安装下载
  • 连云港做网站最好seo是什么工作
  • 做网站的有哪些安卓aso关键词优化
  • 免费建站平台哪个稳定拉人头最暴利的app
  • 中国建设银行注册网站东莞seo网络公司
  • 开传奇私服网站怎么做seo推广
  • 公司做网站哪个好湖南百度推广公司
  • 对单位网站的要求百度推广客户端下载
  • wordpress文件系统插件百度关键词优化平台
  • an网站建设品牌营销策划怎么写
  • 福建疫情最新数据消息郑州seo公司排名
  • 做网站用的什么语言福清市百度seo
  • 网站加入wordpress网站seo优化免费
  • 潍坊网站建设500网站设计公司官网
  • wordpress主题销售seo优化报价公司
  • 网站制作最seo教程免费分享
  • 免费怎么制作公司网站网站策划方案案例
  • 怎么用hbuilder做网站百度爱采购推广效果怎么样?
  • b2c电子商务网站.市场营销是做什么的
  • 门户网站模版市场调研报告最佳范文