Windows下多线程编程技术及其实现(转载)

音频DSP开发中的‘隐藏关卡’:那些SigmaStudio未公开的模块计算秘籍 本文深入探讨音频DSP开发中SigmaStudio未公开的模块计算秘籍,重点解析动态范围控制、均衡器参数映射和混响算法优化等核心模块计算公式。通过实际案例揭示如何利用这些非官方技巧提升音频质量与系统性能,为ADI DSP开发者提供宝贵的实战经验。 阅读详情

本文首先讨论16位Windows下不具备的线程的概念,然后着重讲述在32位Windows 95环境下多线程的编程技术,最后给出利用该技术的一个实例,即基于Windows95下TCP/IP的可视电话的实现。

  一、问题的提出

  作者最近在开发基于Internet网上的可视电话过程中,碰到了这样一个问题。在基于Internet网上的可视电话系统中,同时要进行语音采集、语音编解码、图象采集、图象编解码、语音和图象 码流的传输, 所有的这些事情,都要并行处理。特别是语音信号,如果进行图象编解码时间过长,语音信号得不到服务,通话就有间断,如果图象或语音处理时间过长,而不能及时的传输码流数据,通信同样也会中断。这样就要求我们实现一种并行编程,在只有一个CPU的机器上,也就是要将该CPU时间按照一定的优先准则分配给各个事件,定期处理某一事件而不会在某一事件处理过长,在32位Windows95或WindowsNT下,我们可以用多线程的编程技术来实现这种并行编程。实际上这种并行编程在很多场合下都是必须的。例如,在FileManager拷贝文件时,它显示一个对话框, 列出源文件和目标文件的名称,并在对话框中包含了一个Cancel按钮。如果在文件拷贝过程中,点中Cancel按钮,就会终止拷贝。

  在16位Windows中,实现这类功能需要在FileCopy循环内部周期性地调用PeekMessage函数。如果正在读一个很大的数据块,则只有当这个块读完以后才能响应这个按钮动作,如果从软盘读文件,则要花费好几秒的时间,由于机器反应太迟钝,你会频繁地点中这个按钮,以为系统不知道你想终止这个操作。如果把FileCopy指令放入另外一个线程,你就不需要在代码中放一大堆PeekMessage函数,处理 用户界面的线程将与它分开操作,这样,点中Cancel按钮后会立即得到响应。同样的道理,在应用程序中创建一个单独线程来处理所有打印任务也是很有用的,这样,用户可以在打印处理时继续使用应用程序。

  二、线程的概念

  为了了解线程的概念,我们必须先讨论一下进程的概念。

  一个进程通常定义为程序的一个实例。在Win32中, 进程占据4GB的地址空间。与它们在MS-DOS和16位Windows操作系统中不同, Win32进程是没有活力的。这就是说,一个Win32进程并不执行什么指令,它只是占据着4GB的地址空间,此空间中有应用程序EXE文件的 代码和数据。EXE需要的任意DLL也将它们的代码和数据装入到进程的地址空间。除了地址空间,进程还占有某些资源,比如文件、动态内存分配和线程。当进程终止时,在它生命期中创建的各种资源将被清除。

  但是进程是没有活力的,它只是一个静态的概念。为了让进程完成一些工作,进程必须至少占有一个线程,所以线程是描述进程内的执行,正是线程负责执行包含在进程的地址空间中的代码。实际上,单个进程可以包含几个线程, 它们可以同时执行进程的地址空间中的代码。为了做到这一点,每个线程有自己的一组CPU寄存器和堆栈。

  每个进程至少有一个线程在执行其地址空间中的代码,如果没有线程执行进程 地址空间中的代码, 进程也就没有继续存在的理由,系统将自动清除进程及其地址空间。为了运行所有这些线程,操作系统为每个独立线程安排一些CPU 时间,操作系统以轮转方式向线程提供时间片,这就给人一种假象,好象这些线程都在同时运行。创建一个Win32进程时,它的第一个线程称为主线程,它 由系统自动生成,然后可由这个主线程生成额外的线程,这些线程,又可生成更多的线程。

三、线程的编程技术

  1、编写线程函数

  所有线程必须从一个指定的函 数开始执行,该函数称为线程函数,它必须具有下列原型:

  DWORDWINAPIYourThreadFunc(LPVOIDlpvThreadParm);

  该函数输入一个LPVOID型的参数,可以是一个DWORD型的整数,也可以是一个指向一个缓冲区的指针, 返回一个DWORD型的值。象WinMain函数一样,这个函数并不由操作系统调用, 操作系统调用包含在KERNEL32.DLL中的非C运行时的一个内部函数,如StartOfThread,然后由StartOfThread函数建立起一个异常处理框架后,调用我们的函数。

  2、创建一个线程

  一个进程的主线程是由操作系统自动生成,如果你要让一个主线程创建额外的线程,你可以调用来CreateThread完成。

  HANDLECreateThread(LPSECURITY_ATTRIBUTES lpsa,DWORDcbstack,LPTHREAD_START_ROUTINElpStartAddr,
  LPVOID lpvThreadParm,DWORDfdwCreate,LPDWORDlpIDThread);

  其中lpsa参数为一个指向SECURITY_ATTRIBUTES结构的指针。如果想让对象为缺省安全属性的话,可以传一个NULL,如果想让任一个子进程都可继承一个该线程对象句柄,必须指定一个SECURITY_ATTRIBUTES结构,其中bInheritHandle成员初始化为TRUE。参数cbstack表示线程为自己所用堆栈分配的地址空间大小,0表示采用系统缺省值。

  参数lpStartAddr用来表示新线程开始执行时代码所在函数的地址,即为线程函数。lpvThreadParm为传入线程函数的参数,fdwCreate参数指定控制线程创建的附加标志,可以取两种值。如果该参数为0,线程就会立即开始执行,如果该参数为CREATE_SUSPENDED,则系统产生线程后,初始化CPU,登记CONTEXT结构的成员,准备好执行该线程函数中的第一条指令,但并不马上执行,而是挂起该线程。最后一个参数lpIDThread 是一个DWORD类型地址,返回赋给该新线程的ID值。

  3、终止线程

  如果某线程调用了ExitThread 函数,就可以终止自己。

  VOIDExitThread(UINTfuExitCode );

  这个函数为调用该函数的线程设置了退出码fuExitCode后, 就终止该线程。调用TerminateThread函数亦可终止线程。

  BOOLTerminateThread(HANDLE hThread,DWORDdwExitCode);

  该函数用来结束由hThread参数指定的线程, 并把dwExitCode设成该线程的退出码。当某个线程不在响应时,我们可以用其他线程调用该函数来终止这个不响应的线程。

  4、设定线程的相对优先级

  当一个线程被首次创建时,它的优先级等同于它所属进程的优先级。在单个进程内可以通过调用SetThreadPriority函数改变线程的相对优先级。一个线程的优先级是相对于其所属的进程的优先级而言的。

  BOOLSetThreadPriority(HANDLE hThread,intnPriority);

  其中参数hThread是指向待修改 优先级线程的句柄,nPriority可以是以下的值:

  THREAD_PRIORITY_LOWEST,
  THREAD_PRIORITY_BELOW_NORMAL,
  THREAD_PRIORITY_NORMAL,
  THREAD_PRIORITY_ABOVE_NORMAL,
  THREAD_PRIORITY_HIGHEST

  5、挂起及恢复线程

  先前我提到过可以创建挂起状态的线程(通过传递CREATE_SUSPENDED标志给函数CreateThread来实现)。当你这样做时,系统创建指定线程的核心对象,创建线程的栈,在CONTEXT结构中初始化线程CPU注册成员。然而,线程对象被分配了一个初始挂起计数值1,这表明了系统将不再分配CPU去执行线程。要开始执行一个线程,另一个线程必须调用ResumeThread并传递给它调用CreateThread时返回的线程句柄。

  DWORD ResumeThread(HANDLEhThread);

  一个线程可以被挂起多次。如果一个线程被挂起3次, 则该线程在它被分配CPU之前必须被恢复3次。除了在创建线程时使用CREATE_SUSPENDED标志,你还可以用SuspendThread函数挂起线程。

  DWORDSuspendThread(HANDLE hThread); 

四、多线程编程技术的应用

  我在前面说过,为了实现基于TCP/IP下的可视电话,就必须“并行”地执行语音采集、语音编解码、图象采集、图象编解码以及码流数据的接收与发送。语音与图象的采集由硬件采集卡进行,我们的程序只需初始化该硬件采集卡,然后实时读取采集数据即可,但语音和图象数据的编解码以及码流数据的传输都必须由程序去协调执行,决不能在某一件事件上处理过长,必须让CPU轮流的为各个事件服务,Windows95下的线程正是满足这种要求的编程技术。

  下面我给出了利用Windows95 环境下的多线程编程技术实现的基于TCP/IP的可视电话的部分源码,其中包括主窗口过程函数,以及主叫端与被叫端的TCP/IP接收线程函数和语音编解码的线程函数。由于图象编解码的实时性比语音处理与传输模块的实时性的 要求要低些,所以我以语音编解码为事件去查询图象数据,然后进行图象编解码,而没有为图象编解码去单独实现一个线程。

  在主窗口初始化时, 我用CREATE_SUSPENDED标志创建了两个线程hThreadG7231和hThreadTCPRev。一个用于语音编解码,它的线程函数为G723Proc, 该线程不断查询本地有无编好码的语音和图象的码流,如有,则进行H.223打包,然后通过TCP的端口发送给对方。另外一个线程用于TCP/IP的接收,它的线程函数为AcceptThreadProcRev,该线程不断侦 测TCP/IP端口有无对方传来的码流,如有,就接收码流,进行H.223解码后送入相应的缓冲区。该缓冲区的内容,由语音编解码线程G723Proc查询,并送入相应的解码器。由于使用了多线程的编程技术,使得操作系统定时去服务语 音编解码模块和传输模块,从而保证了通信的不中断。

  五、程序源码

 

//基于TCP/IP可视电话主窗口的窗口过程
LONG APIENTRY MainWndProc(HWND hWnd,UINT message,UINT wParam, LONG lParam)
{
 static HANDLE hThreadG7231,hThreadTCPListen,hThreadTCPRev;
 DWORDThreadIDG7231,ThreadIDTCPListen,ThreadIDTCPRev;
 static THREADPACK tp;
 static THREADPACK tp1;
 unsigned char Buf[80];
 CAPSTATUS capStatus;
 switch (message)
  {
   case WM_CREATE:
     Init_Wsock(hWnd); //初始化一些数据结构
     Init_BS(2,&bs);
     vd_tx_pdu.V_S = 0;vd_tx_pdu.N_S = 0;
     vd_rx_pdu.V_R = 0;vd_tx_sdu.bytes = 0;
     if( dnldProg ( hWnd, "h324g723.exe") )
      {
       //装入语音编解码的DSP核心
       MessageBox(hWnd,"Load G.723.1 Kernel Error","Error",MB_OK);
       PostQuitMessage(0); }
     else
       MessageBox(hWnd,"Load G.723.1 Kernel OK!","Indication",MB_OK);
     //创建语音编解码的线程
     parag7231.hWnd = hWnd;
     hThreadG7231=CreateThread (NULL, 0,(LPTHREAD_START_ROUTINE)G723Proc,
                   (G7231DATA *)?g7231,
                   CREATE_SUSPENDED,(LPDWORD)&ThreadIDG7231);
     if (!hThreadG7231)
      {
       wsprintf(Buf, "Error in creating G7231 thread: %d",GetLastError());
       MessageBox (hWnd, Buf, "WM_CREATE", MB_OK);}
     //创建TCP/IP接收线程
     tp1.hWnd = hWnd;
     hThreadTCPRev = CreateThread (NULL, 0,(LPTHREAD_START_ROUTINE)AcceptThreadProcRev,
                    (G7231DATA *)&tp1,CREATE_SUSPENDED,
                    (LPDWORD)&ThreadIDTCPRev);
     if (!hThreadTCPRev)
      {
       wsprintf(Buf, "Error in creating TCP Receive thread: %d",GetLastError());
       MessageBox (hWnd, Buf, "WM_CREATE", MB_OK);}
     //开始侦听网络
     SendMessage(hWnd,WM_COMMAND,IDM_LISTEN,NULL);
     break;
   case WM_VIDEO_ENCODE: //图象编码
     if(needencode)EncodeFunction(hWnd);
     needencode = SendVideoToBuff(&vd_tx_sdu, buff);
     frameMode=TRUE;
     capPreview(capWnd,FALSE);
     capOverlay(capWnd,FALSE);
     capGrabFrameNoStop(capWnd);
     break;
   case WM_VIDEO_DECODE: //图象解码
     Video_Decod_begin = 1;
     play_movie();
     Video_Decod_begin = 0;
     break;
   case WM_COMMAND:
     switch(LOWORD(wParam))
      {
       case IDM_CONNECT: //响应对方的呼叫,接通可视电话
         WskConnect( hWnd );
         ResumeThread(hThreadTCPRev); //运行TCP/IP接收线程
         ResumeThread(hThreadG7231); //运行语音编解码线程
         BeginG7231Codec(); //初始化图象采集卡,并开始采集图象
         frameMode = FALSE;
         capWnd = capCreateCaptureWindow((LPSTR)"Capture Window",
                          WS_CHILD | WS_VISIBLE,
                          100, 100, 176,144 ,
                          (HWND) hWnd, (int) 0);
         capSetCallbackOnError(capWnd, (FARPROC)ErrorCallbackProc) ;
         capSetCallbackOnStatus(capWnd, (FARPROC)StatusCallbackProc) ;
         capSetCallbackOnFrame(capWnd, (FARPROC)FrameCallbackProc) ;
         capDriverConnect(capWnd, 0);
         CenterCaptureWindow(hWnd, capWnd);
         capDlgVideoSource(capWnd);
         capDlgVideoFormat(capWnd);
         capDlgVideoCompression(capWnd);
         capGetStatus(capWnd,&capStatus,sizeof(CAPSTATUS));
         StartNewVideoChannel(hWnd, capWnd) ;
         image = image_one;
         frameMode = TRUE;
         capPreview(capWnd,FALSE);
         capOverlay(capWnd,FALSE);
         capGrabFrameNoStop(capWnd);
         break;
       case IDM_LISTEN: //拨对方号码,呼叫对方
         sock = socket( AF_INET, SOCK_STREAM, 0);
         if (sock == INVALID_SOCKET) {
          MessageBox(hWnd, "socket() failed", "Error", MB_OK);
          closesocket(sock);
          break;}
         if (!FillAddr(hWnd, &local_sin, FALSE )) //获取TCP/IP地址和端口号
          break;
         EnableMenuItem(GetMenu( hWnd ), IDM_LISTEN, MF_GRAYED);
         SetWindowText( hWnd, "Waiting for connection..");
         bind ( sock , (struct sockaddr FAR *)&local_sin,sizeof(local_sin);
         if (listen( sock, MAX_PENDING_CONNECTS ) <0)
          {
           sprintf(szBuff, "%d is the error",
               WSAGetLastError()); MessageBox(hWnd, szBuff, "listen(sock) failed",
               MB_OK);
           break;}
         tp.hWnd="hWnd; //开始本地的TCP/IP接收线程"
         _beginthread(AcceptThreadProc,0,&tp);
         ResumeThread(hThreadG7231); // 开始本地语音编解码的线程
         break;
       case IDM_DISCONNECT: //挂断可视电话
         CloseG7231Codec();
         SuspendThread(hThreadG7231);
         SuspendThread(hThreadTCPRev);
         WSACleanup();
         Init_Video_Decod_Again();
         capSetCallbackOnError(capWnd, NULL);
         capSetCallbackOnStatus(capWnd, NULL);
         InvalidateRect(hWnd,NULL,1); capSetCallbackOnFrame(capWnd, NULL);
         capSetCallbackOnVideoStream(capWnd, NULL);
         capDriverDisconnect(capWnd);
         Init_Wsock(hWnd);
         MessageBox(hWnd, "Now closing the Video telephone","",MB_OK);
         SetDisConnectMenus(hWnd);
         SendMessage(hWnd, WM_COMMAND,IDM_LISTEN,NULL);
         break;
       case IDM_EXIT:
         CloseG7231Codec();
         SendMessage(hWnd, WM_CLOSE, 0, 0l);
         break; default:
      return (DefWindowProc(hWnd, message, wParam, lParam));
      }
      break;
    case WM_CLOSE:
      if (IDOK !="MessageBox(" hWnd, "OK to close window?", gszAppName,
                  MB_ICONQUESTION | MB_OKCANCEL ))break ;
    case WM_DESTROY:
      WSACleanup();
      CloseG7231Codec();
      TerminateThread(hThreadG7231,0);
      TerminateThread(hThreadTCPRev,0);
      capSetCallbackOnError(capWnd, NULL);
      capSetCallbackOnStatus(capWnd, NULL);
      capSetCallbackOnFrame(capWnd, NULL);
      capSetCallbackOnVideoStream(capWnd, NULL);
      capDriverDisconnect(capWnd);
      FreeAll();
      PostQuitMessage(0);
      break;
   default: /* Passes it on if unproccessed */
       return (DefWindowProc(hWnd, message, wParam, lParam));
   }
 return (0);
}

//主叫方TCP/IP接收线程
DWORD WINAPI AcceptThreadProc( PTHREADPACK ptp )
{
 SOCKADDR_IN acc_sin; /* Accept socket address internet style */
 int acc_sin_len; /* Accept socket address length */
 int status;
 acc_sin_len="sizeof(acc_sin);"
 //调用阻塞函数accept,一直到远端响应为止
 sock="accept(" sock,(struct sockaddr FAR *) &acc_sin,(int FAR *) &acc_sin_len );
 if (sock < 0)
  {
   sprintf(szBuff, "%d is the error", WSAGetLastError());
       MessageBox(ptp->hWnd, szBuff, "accept(sock) failed", MB_OK);
   return (1);
  }
 SetConnectMenus( ptp->hWnd ); //远端提机,可视电话接通
 BeginG7231Codec();
 while (1)
  {
   beg1:
    status = recv((SOCKET)sock, r_mux_buf,MY_MSG_LENGTH, NO_FLAGS_SET );
   if (status == SOCKET_ERROR) {
    status = WSAGetLastError();
    if( status == 10054 ){
     MessageBox(ptp->hWnd,"对方挂断电话","Indication", MB_OK);
     SendMessage( ptp->hWnd, WM_COMMAND,IDM_DISCONNECT,NULL);
     _endthread();
     return (1);
     }
    goto beg1;
    }
   if (status) {
     r_mux_buf[ status ] = '/0';
     if ( r_mux_buf_filled == 1 )
      r_mux_buf_overwrite = 1;
     else
     r_mux_buf_filled = 1;
     r_mux_buf_length = status;
    }
   else
    {
     MessageBox( hWnd, "Connection broken", "Error", MB_OK);
     SendMessage( ptp->hWnd, WM_COMMAND,IDM_DISCONNECT,NULL);
     _endthread();
     return (2);
    }
   demux(); //线路码流H.223解码
   }
 return (0);
}

//被叫方TCP/IP接收线程
DWORD WINAPI AcceptThreadProcRev( PTHREADPACK ptp )
{
 int status;
 while (1)
  {
  beg2:
  status = recv((SOCKET)sock, r_mux_buf,MY_MSG_LENGTH, NO_FLAGS_SET );
  if (status == SOCKET_ERROR)
   {
    status =WSAGetLastError();
    if( status == 10054 )
     {
      MessageBox(ptp->hWnd,"对方挂断电话","Indication", MB_OK);
      SendMessage( ptp->hWnd, WM_COMMAND,IDM_DISCONNECT,NULL);
      return (1);
     }
    goto beg2;
    }
  if (status)
   {
    r_mux_buf[ status ] = '/0';
    if( r_mux_buf_filled == 1 )
     r_mux_buf_overwrite = 1;
    else
     r_mux_buf_filled = 1;
     r_mux_buf_length = status;
   }
  else
   {
    MessageBox( hWnd, "Connection broken", "Error", MB_OK);
    SendMessage( ptp->hWnd, WM_COMMAND,IDM_DISCONNECT,NULL);
    return (2);
   }
  demux();
 } /* while (forever) */
 return (0);
}

//语音编解码线程
DWORD WINAPI G723Proc(G7231DATA *data)
{
 int i,len;
 Audio_tx_pduad_tx_pdu;
 unsigned char mux[MAX_MUX_PDU_SIZE];
 do
  {
   len = 0;
   //检测本地有无语音,图象码流要传输
   i = DetectAudioVideoData();
   switch(i)
    {
     case AUDIO_ONLY: //只有语音码流
       AL2_CRC_coder(&ad_tx_sdu,&ad_tx_pdu);
       //H.223打包
       len = AL2_To_MUX(&ad_tx_pdu, mux);
       break;
     case VIDEO_ONLY: //只有图象码流
       SDU_To_PDU(&vd_tx_sdu,&vd_tx_pdu);
       tx_AL3_I_PDU(&vd_tx_pdu ,&bs , 1); //H.223打包
       len = AL3_To_MUX(&vd_tx_pdu,mux);
       break;
     case AUDIO_VIDEO: //语音和图象码流
       AL2_CRC_coder(&ad_tx_sdu,&ad_tx_pdu);
       SDU_To_PDU(&vd_tx_sdu,&vd_tx_pdu);
       tx_AL3_I_PDU(&vd_tx_pdu ,&bs , 1);
       //H.223打包
       len = AL2_AL3_To_MUX(&ad_tx_pdu,&vd_tx_pdu,mux);
       break;
     case NO_AUDIO_VIDEO: //此刻无码流要传输
       break;
    }
   //TCP/IP发送码流
   if(len != 0)
   send((SOCKET)sock,mux,len,0);
   //是否接收到待解码的码流,有就调用解码器
   PutVideoStreamToDecod();
  }
 while(1);
 return (0);
}



 

ESP32设备驱动-TB6612FNG直流电机驱动 TB6612FNG 是一款出色的双电机驱动器,非常适合将两个小型直流电机(例如微型金属齿轮电机)连接到微控制器,它还可以用于控制单个双极步进电机。 基于 MOSFET 的 H 桥比 L298N 和 Sanyo 的 LB1836M 等旧式驱动器中使用的基于 BJT 的 H 桥效率高得多,后者允许将更多电流输送到电机,而从逻辑电源汲取的电流更少 (对于真正低电压的应用,LB1836 仍然具有 TB6612 的优势)。 阅读详情

相关推荐

最终篇——SWAT模型的建立

同理,Soil Data也是同样的方法导入,在Soil Database Options的选项中,选择UserSiol,用同样的方法导入土壤类型,进行重分类。打开窗口后,只定义HRU Thresholdes,其余两个不用设置,设定各数据单元的计算率,这里的10%的意思是计算精度,即分成级HRU计算单元太小,小于平均计算单元的10%,我们就不对这HRU单元进行计算(也就是说太破碎的计算单元不进行计算),若选择0%意味着全部进行计算,需要耗费大量的内存和时间,对计算机要求较高,不建议。

m0_72556434的博客 5621

Windows C++ 多线程编程示例

/* 题目:主线程创建两个辅助线程,辅助线程1使用选择排序算法对数组的前半部分排序,辅助线程2使用选择排序算法对数组的后半部分排序,主线程等待辅助线程运行結束后,使用归并排序算法归并子线程的计算结果 开发工具:DEV windows平台 语言:C++ */ #include <windows.h> #include <iostream> using namespace std; #define MAX 100 //数组空间最大值 int array[MAX]; //待排

Alkali!的博客 5490

西门子触摸屏教程wincc flexible2008 sp5 Smart V3 V4中文版软件

西门子触摸屏教程wincc flexible2008 sp5 Smart V3 V4中文版软件

liang007dudu的博客 1万+

多线程编程10例

进程和线程都是操作系统的概念。进程是应用程序的执行实例,每个进程是由私有的虚拟地址空间、代码、数据和其它各种系统资源组成,进程在运行过程中创建的资源随着进程的终止而被销毁,所使用的系统资源在进程终止时被释放或关闭。线程是进程内部的一个执行单元。系统创建好进程后,实际上就启动执行了该进程的主执行线程,主执行线程以函数地址形式,比如说main或WinMain函数,将程序的启动点提供给Windows系统。主执行线程终止了,进程也就随之终止。

1164

强化学习 | Multi Agents | Trust Region | HATRPO | HAPPO

ICLR2022多智能体论文,将trust region推广到MA上,保证单调递增。学习博客,共7k字,纯手敲。含大量手写笔记。

111辄的博客 1万+

EPLAN电气设计精华 -模拟量传感器画法全解

本文介绍了EPLAN中模拟量压力传感器的标准画法要点,重点解析了电流型(4-20mA)、电压型(0-10V/0-5V)和特定应用型三种传感器的核心特点、典型符号、接线方式及应用场景。文章提供了EPLAN设备标识符示例,并分享了一个实际项目案例(XJC-Y01-20-4M传感器配合XJC-608T-F变送器和GSEE.GXLK-AI4S-I模块的应用),强调绘制前查阅技术资料的重要性。内容简明实用,有助于规范电气设计中的传感器绘制。

Qinmingyue13的博客 1182

Windows下多线程编程技术及其实现

  本文首先讨论16位Windows下不具备的线程的概念,然后着重讲述在32位Windows 95环境下多线程的编程技术,最后给出利用该技术的一个实例,即基于Windows95下TCP/IP的可视电话的实现。   一、问题的提出   作者最近在开发基于Internet网上的可视电话过程中,碰到了这样一个问题。在基于Internet网上的可视电话系统中,同时要进行语音采集、语音编解码、图象采...

weixin_30692143的博客 233

前端Three.js面试题及参考答案

渲染循环(Render Loop)是 Three.js 中一个非常重要的概念,它是一个不断重复执行的过程,用于更新和渲染 3D 场景。在每次循环中,渲染循环会更新场景中的对象状态,如位置、旋转、缩放等,然后使用渲染器将更新后的场景渲染成 2D 图像显示在屏幕上。通过不断地重复这个过程,就可以实现动画效果。渲染循环的核心作用是确保场景能够实时更新,给用户带来流畅的视觉体验。如果没有渲染循环,场景将是静态的,不会有任何变化。只有通过不断地更新和渲染,才能让场景中的对象动起来,实现各种动画效果。

大模型大数据攻城狮的专栏 2722

【C语言】Windows下的C语言线程编程详解

C语言,线程。

风高秋月白,雨霁晚霞红 7284

【WorkBuddy从入门到精通实战教程】实战案例 第 41 章 论文阅读与文献综述

本文系统讲解如何用WorkBuddy高效阅读论文并撰写文献综述。针对“读了一天没记住”的痛点,提出“三遍阅读法”:快速评估相关性、结构化提炼核心内容、批判性分析局限。结合提示词工程,实现单篇精读、多篇对比与综述框架搭建,显著提升效率。强调学术诚信,严防AI幻觉,要求所有引用与数据必须回源核对。最终目标是借助AI辅助理解与组织,但坚持自主思考与写作,真正掌握科研能力。

企鹅码农的专属播客,分享10多年的实战案例和经验解读,包含YOLO、RT-DETR、AI等各个工作领域。 140

ResNet改进(51):基于轴向注意力机制的改进ResNet模型

本文介绍了一种结合轴向注意力机制的改进ResNet模型。 通过在传统CNN中引入轴向注意力,我们能够在保持计算效率的同时,增强模型对图像全局上下文的理解能力。这种混合架构结合了CNN的局部特征提取优势和注意力机制的全局建模能力,在图像识别任务中表现出色。 轴向注意力机制的应用不仅限于ResNet,也可以灵活地集成到其他CNN架构中。 未来可以探索更多注意力变体,或将其应用于特定领域任务,如医学图像分析、自动驾驶等需要精确空间理解的场景。 希望本文能帮助你理解如何将注意力机制与传统CNN结合,为你的计

2401_82355416的博客 359

C++多线程编程(真实入门)

https://blog.youkuaiyun.com/Kprogram/article/details/89472995

hanxiaoyong_的博客 2669

Python爬虫案例——解密抓取西红柿小说《我不是戏神》全章节!!!

本文介绍了抓取番茄小说网站《我不是戏神》全本内容的完整流程。首先分析网站反爬机制,发现采用自定义混淆字体加密文本内容。通过下载字体文件并解析字符映射关系,组建本地字体映射表。随后编写Python爬虫程序,实现自动获取章节链接、请求加密内容、解密替换字体、保存为本地txt文件等功能。代码包含UA伪装、请求头设置、进度条显示等细节处理,最终实现小说内容的完整抓取与解密存储。

@优快云盲敲代码的阿豪的博客 661

(转载)win32多线程的性能

Win32 多线程的性能出处:MicrosoftRuediger R. AscheMicrosoft Developer Network 技术小组 摘要本文讨论将单线程应用程序重新编写成多线程应用程序的策略。它以Microsoft Windows 95和Windows NT的平台为例,从吞吐量(throughput)和响应方面,与兼容的单线程计算相比较而分析了多线程计算的性能。介绍在您

星光中的灯 4069

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

在C++Builer中多线程的实现(转载)

在C++Builer中多线程的实现 Yinyin   还在Dos时代,人们就在寻求一种多任务的实现。于是出现了TSR类型的后台驻留程序,比较有代表性的有Side Kick、Vsafe等优秀的TSR程序,这类程序的出现和应用确实给用户使用计算机带来了极大的方便,比如Side Kick,我们编程可以在不用进编辑程序的状态下,一边编辑源程序,一边编译运行,非常方便。但是,Dos单任务操作系统的致命缺陷注

825

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

转载:c++builder多线程程序

1. 创建多线程程序:  首先,先介绍一下实现多线程的具体步骤。在C++Builder中虽然用Tthread对象说明了线程的概 念,但是Tthread对象本身并不完整,需要在TThread下新建其子类,并重载Execute方法来使用线程对象。在C++Builder下可以很 方便地实现这一点。  在C++Builder IDE环境下选择菜单File|New,在New栏中选中Thread Object

zhtq301的专栏 2189

如何使用Robocopy多线程功能加速Windows 10中的文件复制(转载)

此时,许多精通技术的 Windows 用户都会使用 Robocopy(Robust File Copy,强大的文件复制),这是一个内置于 Windows 10 中的命令行工具,它提供了更多功能来帮助用户更快地将数据移动到不同的位置。Robocopy 最为特殊也经常被忽略的是其支持多线程的能力,允许我们同时复制多个文件,而不像在「文件管理器」中复制文件时只能一次一个(移动多个文件时也只是列队之后一个一个移动)。我个人推荐使用 32 个线程,虽然可开的线程可以更多,但数字越大,所使用的系统资源和带宽就越多。

bodybo的专栏 4603

windows lua 多线程 线程同步

今天在改一个程序,改成部分逻辑用lua写,这个程序是多线程的。将程序中部分逻辑改成lua之后,各种非法访问内存错误,各种奇奇怪怪的问题,不分时间,不分地点的出现崩溃。从调用堆栈来看,基本都是使用lua造成的。在多线程中使用lua_newthread得到的lus_State仍然有时候程序会崩溃。基本上可以确定为多线程中操作lua 的问题了。 前几天我转载的一篇文章,文章写了关于lua多线程的作法。...

weixin_34240657的博客 238

多线程、方便扩展的Windows服务程序

多线程、方便扩展的Windows服务程序 吴剑 2012-06-02 原创文章,转载必需注明出处:http://www.cnblogs.com/wu-jian/ 前言 在项目应用中经常会碰到定时调度的工作,比如我曾经开发一个日访问量超过1000W的网站,如果这1000W访问都从数据库读取数据显示给用户,我的服务器肯定承受不了,于是我需要每10分钟把首页生成一次.html的静态文...

weixin_30920597的博客 541

Windows CE下的多线程编程实验(转载自Stone& Ice)

Windows CE多线程编程包括线程的启动、线程的运行状态控制、线程同步及数据通信和线程的正常/非正常退出。本项目的软件及架构在多线程设计上,要求通过多线程实现异步的数据采集及绘制,以提高系统运行效率。 1.       线程的启动Win32API提供支持多线程的启动,调用API函数CreateThread()分配资源启动线程,并返回线程句柄(Handle),以控制线程状态。客观上,

gooogleman#foxmail.com 1905

(转载)Win32 多线程的性能

Win32 多线程的性能Ruediger R. AscheMicrosoft Developer Network 技术小组 摘要本文讨论将单线程应用程序重新编写成多线程应用程序的策略。它以Microsoft Windows 95和Windows NT的平台为例,从吞吐量(throughput)和响应方面,与兼容的单线程计算相比较而分析了多线程计算的性能。介绍在您所能够找到的有关多线程的资料

无限天空 1347

转载----实现FTP多线程下载

 http://publishblog.blogchina.com/blog/tb.b?diaryID=3790095现在有不少软件可实现多线程下载.如NetAnts,JetCar等,其实多线程下载的原理并不复杂,主要的两项技术就是多线程和断点下载。程序中开启多个线程,每个线程利用断点下载,分别下载文件的不同部分,下载完后合并,就可以了。多线程编程很多书已有介绍,这里不再重复,关于断点下载,MFC

tanghuaan的专栏 558

vc6多线程-转载

一、问题的提出编写一个耗时的单线程程序:  新建一个基于对话框的应用程序SingleThread,在主对话框IDD_SINGLETHREAD_DIALOG添加一个按钮,ID为IDC_SLEEP_SIX_SECOND,标题为“延时6秒”,添加按钮的响应函数,代码如下:void CSingleThreadDlg::OnSleepSixSecond() { Sleep(6000); //

lang_113的专栏 1487

Windows环境下使用MFC进行多线程编程(C++)

转载请标明出处:http://blog.youkuaiyun.com/zhangxingping Windows环境下使用MFC进行多线程编程(C++) 进程和线程 (Process andThread) 进程指的是计算机程序的一次执行,或者说是程序执行的一个实例。进程中包含有程序代码及其当前的活动。线程指的是进程中的一个执行路径。一个进程有可能是由多个线程组成,这些线程并发地执行程序指令。   在

每天进步一点点。 7579

【转载】linux和windows下的信号量

将win32程序关于多线程的内容移植到Linux下面,不能简单的按照函数对应来移植。不过通过下面的对应关系,再加上你对这些模式的深入了解,相信会移植的很成功。   信号量   Windows 信号量是一些计数器变量,允许有限个线程/进程访问共享资源。Linux POSIX 信号量也是一些计数器变量,可以用来在 Linux 上实现 Windows 上的信号量功能。   信号量的类型:...

dingyi1774的博客 611

linux windows 多线程编程,windows多线程编程_c语言 windows 多线程_linux多线程编程实例...

Windows多线程编程总结关键字:多线程线程同步线程池内核对象1 内核对象1 .1内核对象的概念内核对象是内核分配的一个内存块,这种内存块是一个数据结构,表示内核对象的各种特征。并且只能由内核来访问。应用程序若需要访问内核对象,需要通过操作系统提供的函数来进行,不能直接访问内核对象(Windows从安全性方面来考虑的)。内核对象通过Create*来创建,返回一个用于标识内核对象的句柄,这些句柄(...

weixin_39957951的博客 142

windows下多线程编程: c++ , multithreading and dev-cpp

在Windows下用dev-cpp实现Linux下使用c++进行多线程编程: Dev-cpp开源中国的下载地址http://www.oschina.net/p/dev+cplusplus 一、下载Windows版本的pthread 目前最新版本是:pthreads-w32-2-8-0-release.exe。 主页地址:http://sourceware.org/pt...

a121124的博客 450
上一篇: 浅谈C/C++内存泄漏及其检测工具
下一篇: 半透明及透明位图的实现技术
JamesXing
博客等级 码龄20年 30粉丝 3原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值