VC++深入详解·chapter16·笔记

本文探讨了线程同步机制,包括事件对象、关键代码段等,并介绍了如何使用事件对象来确保应用程序仅运行一个实例。此外,还讲解了基于消息的异步套接字在网络聊天室程序中的应用。

1、事件对象:也属于内核对象(mutex属于内核对象),包含以下三个成员
   1> 使用计数
   2> 用于指明该事件是一个自动重置的事件还是一个人工重置的是件的bool值
   3> 用于指明该事件处于已通知状态还是未通知状态的bool值

2、实现线程同步:利用事件对象
   1> 相关函数:  
   CreateEvent函数:创建或打开一个命名的或匿名的事件对象
   ResetEvent函数:把指定的事件对象设置为无信号状态
   SetEvent函数:把指定的事件对象设置为有信号状态
   2> 为了实现线程间的同步,不应该使用人工重置的事件对象,而应该使用自动重置的事件对象

3、保证应用程序只有一个实例运行:通过创建一个命名的事件对象
   g_hEvent=CreateEvent(NULL, false, true, _T("named"));
   if(g_hEvent)
   {
       if(ERROR_ALREADY_EXISTS==GetLastError())
       {
           cout<<"Only one instance can run!"<<endl;
           return 0;
       }
   }

4、三种线程同步方式:互斥对象、事件对象、关键代码段 (P605)
   1> 在编写多线程并且需要实现线程同步时,首选关键代码段
   2> 另外需要在多个进程间的各个线程间实现同步的话,可以使用互斥对象和事件对象

5、代码:
// Tread_Event.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"

#include <windows.h>
#include <iostream>

using std::cout;
using std::endl;

DWORD WINAPI Fun1Proc(
LPVOID lpParameter
);

DWORD WINAPI Fun2Proc(
LPVOID lpParameter
);

int tickets=100;
HANDLE g_hEvent;
int main()

   //g_hEvent=CreateEvent(NULL, true, false, NULL);
   //SetEvent(g_hEvent);

   //通过命名的事件对象实现只有一个实例运行,必须是命名的事件对象
   g_hEvent=CreateEvent(NULL, false, true, _T("named"));
   if(g_hEvent)
   {
       if(ERROR_ALREADY_EXISTS==GetLastError())
       {
           cout<<"Only one instance can run!"<<endl;
           return 0;
       }
   }
   HANDLE pThread1=CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
   HANDLE pThread2=CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
   CloseHandle(pThread1);
   CloseHandle(pThread2);
   Sleep(40000);
   return 0;
}


DWORD WINAPI Fun1Proc(
LPVOID lpParameter
)
{
   while(true)
   {
       WaitForSingleObject(g_hEvent, INFINITE);
       //ResetEvent(g_hEvent);
       if(tickets>0)
       {
           Sleep(1);
           cout<<"Thread1 sell ticket:"<<tickets--<<endl;

       }
       else
           break;
       SetEvent(g_hEvent);
      
   }
   return 0;
}

DWORD WINAPI Fun2Proc(
LPVOID lpParameter
)
{
   while(true)
   {
       WaitForSingleObject(g_hEvent, INFINITE);
       //ResetEvent(g_hEvent);
       if(tickets>0)
       {
           Sleep(1);
           cout<<"Thread2 sell ticket:"<<tickets--<<endl;

       }
       else
           break;
       SetEvent(g_hEvent);
      
   }
   return 0;
}

6、关键代码段:
#include <windows.h>
#include <iostream>

using std::cout;
using std::endl;

DWORD WINAPI Fun1Proc(
LPVOID lpParameter
);

DWORD WINAPI Fun2Proc(
LPVOID lpParameter
);

int tickets=100;
CRITICAL_SECTION g_cs;
int main()
{
   InitializeCriticalSection(&g_cs);
   HANDLE pThread1=CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
   HANDLE pThread2=CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
   CloseHandle(pThread1);
   CloseHandle(pThread2);
   Sleep(4000);
   DeleteCriticalSection(&g_cs);
   return 0;
}


DWORD WINAPI Fun1Proc(
LPVOID lpParameter
)
{
   while(true)
   {
       EnterCriticalSection(&g_cs);
       if(tickets>0)
       {
           Sleep(1);
           cout<<"Thread1 sell ticket:"<<tickets--<<endl;

       }
       else
           break;
       //LeaveCriticalSection(&g_cs);
      
      
   }
   return 0;
}

DWORD WINAPI Fun2Proc(
LPVOID lpParameter
)
{
  
   while(true)
   {
       EnterCriticalSection(&g_cs);
       if(tickets>0)
       {
           Sleep(1);
           cout<<"Thread2 sell ticket:"<<tickets--<<endl;

       }
       else
           break;
       LeaveCriticalSection(&g_cs);
      
   }
   //cout<<"thread2 is running"<<endl;
   return 0;
}

7、线程死锁:
#include <windows.h>
#include <iostream>

using std::cout;
using std::endl;

DWORD WINAPI Fun1Proc(
LPVOID lpParameter
);

DWORD WINAPI Fun2Proc(
LPVOID lpParameter
);

int tickets=100;
CRITICAL_SECTION g_csA;
CRITICAL_SECTION g_csB;
int main()
{
   InitializeCriticalSection(&g_csA);
   InitializeCriticalSection(&g_csB);
   HANDLE pThread1=CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
   HANDLE pThread2=CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
   CloseHandle(pThread1);
   CloseHandle(pThread2);
   Sleep(4000);
   DeleteCriticalSection(&g_csA);
   DeleteCriticalSection(&g_csB);
   return 0;
}


DWORD WINAPI Fun1Proc(
LPVOID lpParameter
)
{
   while(true)
   {
       EnterCriticalSection(&g_csA);
       Sleep(1); //睡眠1ms,表示放弃CUP执行机会(时间片),跳转到thread2
       EnterCriticalSection(&g_csB);
       if(tickets>0)
       {
           Sleep(1);
           cout<<"Thread1 sell ticket:"<<tickets--<<endl;

       }
       else
           break;
       LeaveCriticalSection(&g_csB);//释放的顺序不影响
       LeaveCriticalSection(&g_csA);
      
   }
   return 0;
}

DWORD WINAPI Fun2Proc(
LPVOID lpParameter
)
{
  
   while(true)
   {
       EnterCriticalSection(&g_csB);
       Sleep(1);//睡眠1ms,表示放弃CUP执行机会(时间片),跳转到thread1
       EnterCriticalSection(&g_csA);
       if(tickets>0)
       {
           Sleep(1);
           cout<<"Thread2 sell ticket:"<<tickets--<<endl;

       }
       else
           break;
       LeaveCriticalSection(&g_csA);
       LeaveCriticalSection(&g_csB);
   }
   //cout<<"thread2 is running"<<endl;
   return 0;
}

8、采用基于消息的异步套接字实现网络聊天室程序
   1>加载套接字库:
   #pragma   comment(lib,   "ws2_32.lib ")
   #include <winsock2.h>
   WORD wVersionRequested;
       WSADATA wsaData;
       int err;
       wVersionRequested = MAKEWORD( 2, 2 );
       err = WSAStartup( wVersionRequested, &wsaData );
       if ( err != 0 ) {
           return false;
       }
       if ( LOBYTE( wsaData.wVersion ) != 2 ||
           HIBYTE( wsaData.wVersion ) != 2 ) {
               WSACleanup( );
               return false;
       }
   添加析构函数:
   CChatSyncApp::~CChatSyncApp()
   {
       WSACleanup();
   }
   2> 创建套接字:
   SOCKET m_socket;
   m_socket=0; //构造函数中
   添加析构函数:
   CChatSyncDlg::~CChatSyncDlg()
   {
       closesocket(m_socket);
   }
   3> 定义套接字初始化函数:
   BOOL CChatSyncDlg::InitSocket()
   {
       m_socket=WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0, 0);
       if(INVALID_SOCKET==m_socket)
       {
           MessageBox(_T("创建套接字失败!"));
           return false;
       }
       SOCKADDR_IN addrSock;
       addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
       addrSock.sin_family=AF_INET;
       addrSock.sin_port=htons(5004);
       if( SOCKET_ERROR==bind(m_socket, (SOCKADDR* )&addrSock, sizeof(SOCKADDR)) )
       {
           MessageBox(_T("绑定失败!"));
           return false;
       }
       //下面的语句为指定套接字请求基于Windows消息的网络事件通知,并自动将该套接字设置为非阻塞模式
       if( SOCKET_ERROR==WSAAsyncSelect(m_socket, this->m_hWnd, UM_SOCK, FD_READ) )
       {
           MessageBox(_T("注册网络读取事件失败!")); //基于Windows消息的网络事件通知
           return false;
       }
       return true;
   }
   并在主Dlg类的OnInitDialog函数中调用,以初始化套接字
   4> 自定义消息及消息相应函数,(自定义消息步骤见chapter09)在消息响应函数中接收消息并显示消息
   LRESULT CChatSyncDlg::OnSock(WPARAM wParam, LPARAM lParam)
   {
       switch(LOWORD(lParam))
       {
       case FD_READ:
           WSABUF wsabuf;
           wsabuf.buf=new char[200];
           wsabuf.len=200;
           SOCKADDR_IN addrFrom;
           int len=sizeof(SOCKADDR);
           DWORD dwRead;
           DWORD dwFlags=0;
           if(SOCKET_ERROR ==WSARecvFrom(m_socket, &wsabuf, 1, &dwRead, &dwFlags, (SOCKADDR* )&addrFrom, &len, 0, 0))
           {
               delete[] wsabuf.buf; //此布易漏
               MessageBox(_T("接收数据失败!"));
               return 0;
           }
           CString str;
           CString strTemp;
           str.Format(_T("%s 说: %s"), inet_ntoa(addrFrom.sin_addr), wsabuf.buf);
           GetDlgItemText(IDC_EDIT_SEND, strTemp); //保存已有数据
           str+="/t/n";
           str+=strTemp;
           SetDlgItemText(IDC_EDIT_RECV, str);
           delete[] wsabuf.buf; //需要手动删除
       }
       return 0;
   }
   5> 实现发送端功能,编写发送按钮的响应函数
   void CChatSyncDlg::OnBnClickedButton1()
   {
       // TODO: 在此添加控件通知处理程序代码
       CString strSend;
       GetDlgItemText(IDC_EDIT_SEND, strSend);
       WSABUF wsabuf;
       int len=strSend.GetLength();
       wsabuf.buf=strSend.GetBuffer(strSend.GetLength());
       wsabuf.len=strSend.GetLength()+1; //多发送一个BYTE
  
       DWORD dwIP;
       ((CIPAddressCtrl* )GetDlgItem(IDC_IP))->GetAddress(dwIP);
       SOCKADDR_IN addrSend;
       addrSend.sin_addr.S_un.S_addr=htonl(dwIP);
       addrSend.sin_family=AF_INET;
       addrSend.sin_port=htons(5004);

       DWORD dwSend;
       if(SOCKET_ERROR ==WSASendTo(m_socket, &wsabuf, 1, &dwSend, 0, (SOCKADDR* )&addrSend, sizeof(SOCKADDR), NULL, NULL))
       {
           MessageBox(_T("发送数据失败!"));
           return ;
       }
       SetDlgItemText(IDC_EDIT_SEND, _T(""));

   }
   注:本程序采用了异步套接字机制在同一线程中完成了接收端和发送端的功能。
   在编写网络应用程序时,采用异步选择机制可以提高网络应用程序的性能,如果再配合多线程技术,将打打提高所编写的网络应用程序的性能。

9、通过IP地址获得主机名:
   HOSTENT* pHost;
   pHost=gethostbyaddr((char* )&addrFrom.sin_addr.S_un.S_addr, 4, AF_INET);
   str.Format(_T("%s 说: %s"), pHost->h_name, wsabuf.buf); //pHost->h_name即为所得主机名

10、通过主机名获得IP地址:
   HOSTENT* pHost=gethostbyname(strHostName);
   addrSend.sin_addr.S_un.S_addr=*((DWORD* )pHost->h_addr_list[0]); //主机名所对应的IP中的第一个IP地址

11、因为网络状况瞬息万变,所以总是应该对函数的返回值进行判断,如果发生错误就要进行相应处理。
    在Windows平台下,为了编写高性能的网络应用程序,出了要对协议本身有所了解以外,还需要了解网络应用程序在Windows平台下工作的原理。

源码直接下载地址: https://pan.quark.cn/s/a4b39357ea24 过采样与欠采样构成了数字信号处理领域中两种基础的采样策略,它们在工程实践应用时各自展现出独特的长处与短处及适用情境。以下将深入阐释这两种采样方法的运作机制,并对它们在实际操作中的区别进行细致对比。 我们首先阐释过采样的核心概念。过采样(Oversampling)一般是指运用高于必要标准频率对模拟信号实施采样。举例而言,当信号频率为70MHz且信号带宽为20MHz时,依据奈奎斯特采样准则,理论上采样频率只需略高于40MHz(即信号带宽频率的两倍)即可达成无失真采样。然而,在现实操作中,系统构造者常常会采用超过140MSPS(每秒百万次采样)的采样速率,这通常超出理论所需。过采样的主要不利之处涵盖:提升ADC输出数据速率,引发FPGA的时序挑战;增大功耗、ADC及FPGA的制造成本。尽管存在这些不足,过采样依然具备其有利之处,例如可提供处理增益、频率规划的伸缩性以及能够处理更宽的信号带宽。 接下来,我们探讨欠采样的基本原理。欠采样(Undersampling)是指以低于理论标准频率对信号进行采样,这在处理高输入信号频率时尤为有效。例如,针对70MHz的中频(IF)信号,通过欠采样能够采用低于40MHz的采样频率进行采样,从而将数据速率降至FPGA,减少时序挑战,节省能量消耗和成本。实现欠采样的关键设计考量在于它能够在系统设计中达成所需的ADC动态性能。 欠采样的优势体现为能够简化硬件构造,比如降低对高速数据捕获的需求,并且在设计条件允许时,可选用较慢的ADC来削减成本。然而,欠采样技术也存在其局限性,例如在ADC的非理想表现可能导致非线性失真,诸如二阶(HD2)和三阶(HD3)谐...
源码链接: https://pan.quark.cn/s/3523d8c4b5d2 ### Qt5.9.1开发的应用程序转换为可安装`.exe`文件的详细流程 #### 一、概述 本资料将系统性地阐述如何将基于Qt5.9.1版本或其他Qt框架版本开发的应用程序转化为可直接安装的`.exe`安装文件。这一过程不仅适用于Qt5.9.1版本,对其他版本的Qt框架开发的应用同样适用。 #### 二、前期准备 在开展相关操作前,需确保已达成以下准备要求: 1. **开发环境配置**: 利用Qt5.9.1或其他版本完成应用程序的开发工作,并保证能够顺利编译出可执行程序。 2. **NSIS安装**: NSIS(Nullsoft Scriptable Install System)作为一个开源的Windows安装系统,能够支持创建专业的安装程序。用户可从官方渠道或可靠来源获取最新版的NSIS并进行安装。 #### 三、制作可执行程序的流程 ##### 3.1 打包应用程序文件 需要将已开发好的Qt应用程序的所有组件和资源整合到一个文件夹中,例如命名为`Qt_Video`。确保该文件夹内包含所有必要的库文件和资源文件,以便应用程序能够独立运行。 ##### 3.2 压缩文件随后,将整个`Qt_Video`文件夹压缩成`.zip`格式的文件。这一步骤可通过Windows内置的压缩工具或第三方软件完成。 ##### 3.3 创建安装文件接下来,借助NSIS将压缩文件转化为安装文件。具体操作如下: 1. **启动NSIS**: 运行NSIS软件并进入其主界面。 2. **选择基于ZIP的安装模式**: 在主界面中选取“**Installer based on ZIP file**...
内容概要:本文介绍了一种结合单像素检测与数据融合技术的千亿体素级多维荧光成像方法,并提供了完整的Matlab代码实现。该方法融合压缩感知理论与单像素成像原理,通过优化测量矩阵设计、重构算法及多维度数据融合策略,实现了在大幅降低数据采集量的前提下,完成高分辨率、高通量的三维荧光成像,特别适用于大规模生物样本的快速、高效成像需求。文中系统阐述了成像系统的建模过程、关键算法的设计思路以及重建性能的优化路径,充分展现了其在超高体素规模下的成像能力与精确重构优势。; 适合人群:面向具备信号处理、光学成像或生物医学工程等相关专业背景的研究生、科研人员及工程技术开发者,尤其适合熟悉Matlab编程并致力于先进成像技术研究与算法复现的专业人士。; 使用场景及目标:①应用于大规模生物组织的三维荧光成像,显著提升成像效率与图像质量;②为单像素成像、压缩感知与多源数据融合等前沿技术提供可复现、可扩展的算法框架;③支撑高维医学影像重建、新型显微成像系统开发及相关科研与工程实践。; 阅读建议:建议结合所提供的Matlab代码进行模块化分析,重点理解测量过程的数学建模与图像重构算法的实现细节,宜在掌握基本理论的基础上开展仿真实验与参数调优,以深入把握核心技术原理与工程实现要点。
下载代码方式:https://pan.quark.cn/s/a4b39357ea24 Node.js 是一种开放源代码且能够在多种操作系统上运行的 JavaScript 执行环境,它使得开发人员能够在服务器端执行 JavaScript 代码。Node.js 采用了 V8 引擎,该引擎是由 Google 为 Chrome 浏览器开发的一个高性能的 JavaScript 解释器。Node.js 的 16.x 版本在其发展历程中占据着重要位置,其中包含了众多新功能以及性能上的改进。标题 "Nodejs16-x64 windows安装包" 指向的是专为 Windows 操作系统设计的 64 位版本的 Node.js 16 安装程序。在 Windows 平台上安装 Node.js 的 64 位版本对于处理大量数据或运行需要高性能的应用程序来说尤为关键,因为 64 位系统能够更有效地利用硬件资源。描述 "Nodejs-16 x64位windows 安装包" 明确了该安装程序是为 Windows 用户准备的,特别是对于那些需要运行 64 位应用程序的用户。x64 表明该版本兼容 64 位架构,意味着它能够充分利用 64 位计算机的内存和处理能力。标签 "Node Nodejs nodejs16" 提供了关于此安装包的核心信息,表明它与 Node.js 相关,并且具体指的是 v16 版本。这些标签有助于进行搜索和分类,从而方便用户找到他们所需要的特定版本。压缩包文件 "node-v16.18.0-x64.msi" 代表实际的安装文件,其中 "v16.18.0" 指示了 Node.js 的具体版本号,"x64" 再次强调了其适用于 64 位系统,而 ".msi" 后缀表明这是一...
源码链接: https://pan.quark.cn/s/3af847fbbec7 在计算机科学与编程领域中,十六进制(Hexadecimal)以及二进制(Binary)是两种关键性的数值表示方法。十六进制属于一种基于16的计数系统,它运用0至9的数字以及字母A至F(分别象征10至15的数值)来呈现数值,与此同时,二进制则是一种基于2的计数系统,仅采用0和1两个符号。掌握这两种进制之间的相互转换对于深入理解计算机内部运作机制具有决定性意义,因为计算机在底层数据的存储与处理环节通常都是以二进制的形式来进行的。将十六进制转换成二进制的过程可以通过以下几个环节得以完成: 1. **单个十六进制符号的转换**:每一个十六进制符号对应着4位二进制序列。具体而言: - 十六进制中的`0`在二进制表达为`0000` - 十六进制中的`1`在二进制表达为`0001` - 十六进制中的`2`在二进制表达为`0010` - 依此类推 - 十六进制中的`9`在二进制表达为`1001` - 十六进制中的`A`或`a`在二进制表达为`1010` - 十六进制中的`B`或`b`在二进制表达为`1011` - 十六进制中的`C`或`c`在二进制表达为`1100` - 十六进制中的`D`或`d`在二进制表达为`1101` - 十六进制中的`E`或`e`在二进制表达为`1110` - 十六进制中的`F`或`f`在二进制表达为`1111` 2. **多位十六进制符号的转换**:针对一个由多个十六进制符号组成的数值,我们可以逐个符号进行转换,并将得到的二进制序列依次拼接。例如,十六进制数`3F`转换成二进制形式为`00111111`。 3. **编程实现方法**:在编程实践过程中,众多编程语言提...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值