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

C++多线程几种方法 在 Windows 平台上,C++ 可以使用多种方法来实现多线程编程。 阅读详情
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>   本文首先讨论16位Windows下不具备的线程的概念,然后着重讲述在32位Windows 95环境下多线程的编程技术 ,最后给出利用该技术的一个实例,即基于Windows95下TCP/IP的可视电话的实现。   关键词:16位Windows环境,32位Windows95环境,进程,线程,TCP/IP,可视电话   目录   1 问题的提出   2 线程的概念   3 线程的编程技术   4 多线程编程技术的应用   5 程序源码 一、问题的提出   作者最近在开发基于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 时间,操作系统以轮转方式向线程提供时间片,这就给人一种假象,好象这些线程都在同时运行。创建一个Wi n32进程时,它的第一个线程称为主线程,它 由系统自动生成,然后可由这个主线程生成额外的线程,这些线程, 又可生成更多的线程。 三、线程的编程技术   1、编写线程函数   所有线程必须从一个指定的函 数开始执行,该函数称为线程函数,它必须具有下列原型:     DWORDWINAPIYourThreadFunc(LPVOIDlpvThreadParm);   该函数输入一个LPVOID型的参数,可以是一个DWORD型的整数,也可以是一个指向一个缓冲区的指针, 返回一 个DWORD型的值。象WinMain函数一样,这个函数并不由操作系统调用, 操作系统调用包含在KERNEL32.DLL中的非C 运行时的一个内部函数,如StartOfThread,然后由StartOfThread函数建立起一个异常处理框架后,调用我们的函 数。   2、创建一个线程   一个进程的主线程是由操作系统自动生成,如果你要让一个主线程创建额外的线程,你可以调用来CreateThre ad完成。   HANDLECreateThread(LPSECURITY_ATTRIBUTES lpsa,DWORDcbstack,LPTHREAD_START_ROUTINElpStartAddr,             LPVOID lpvThreadParm,DWORDfdwCreate,LPDWORDlpIDThread);   其中lpsa参数为一个指向SECURITY_ATTRIBUTES结构的指针。如果想让对象为缺省安全属性的话,可以传一个N ULL,如果想让任一个子进程都可继承一个该线程对象句柄,必须指定一个SECURITY_ATTRIBUTES结构,其中bInher itHandle成员初始化为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、设定线程的相对优先级   当一个线程被首次创建时,它的优先级等同于它所属进程的优先级。在单个进程内可以通过调用SetThreadPri ority函数改变线程的相对优先级。一个线程的优先级是相对于其所属的进程的优先级而言的。   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); }
Windows API 多线程编程 并行化程序开发流程,线程的创建,终止,等待,以及数据竞争问题及相关解决办法 立即下载

相关推荐

IAR for ARM 搭建STM32F103C8T6工程模板(实战指南)

本文详细介绍了如何在IAR for ARM开发环境中为STM32F103C8T6微控制器搭建完整的工程模板。从环境准备、固件库获取、目录结构设计到工程配置与编译调试,提供了实战指南和常见问题解决方案,帮助开发者快速建立稳定的STM32开发环境。

fun88的博客 990

java中的多线程1(基本实现)

进程与线程 进程就是一个程序运行的时候被cpu抽象出来的,一个程序运行后被抽象为一个进程。对于操作系统来讲,一个任务就是一个进程,比如打开一个浏览器就是启动一个浏览器进程。 线程是一个进程里面分割出来的,由于CPU处理进程的时候是采用时间片轮转的方式,所以要把一个大个进程给分割成多个线程。 进程和线程的关系是:线程属于进程,线程运行在进程空间内,同一进程所产生的线程共享一个内存空间,当进程退出时...

都怪这月色的专栏 261

Rockchip平台Android相机HAL3开发实战:从配置到调试全流程解析

本文深入解析了Rockchip平台Android相机HAL3的开发全流程。从理解AAL与PSL核心架构、解析关键配置文件(如camera3_profiles.xml和Tuning文件),到搭建编译环境、进行增量部署,并提供了系统日志分析、V4L2调试等高级问题排查技巧,为开发者提供了一套从配置到调试的实战指南。

docker7nomad的博客 844

Windows平台下的多线程编程(收藏)

线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享所有的进程资源,包括打开的文件、信号标识及动态分配的内存等。一个进程内的所有线程使用同一个地址空间,而这些线程的执行由系统调度程序控制,调度程序决定哪个线程可执行以及什么时候执行线程。线程有优先级别,优先权较低的线程必须等到优先权较高的线程执行完后再执行。在多处理器的机器上,调度程序可将多个线程 放到不同的处理器上去

aheadyes的专栏 1154

手把手教你用TBtools+MCScanX完成玉米基因家族共线性分析(附完整代码)

本文提供了一套完整的玉米基因家族共线性分析实战流程,详细讲解了如何从数据准备、格式处理开始,利用DIAMOND进行快速比对,再结合MCScanX进行核心共线性区块鉴定,最后通过TBtools实现从全基因组圈图到特定基因家族微共线性图的可视化。教程包含可直接运行的代码与常见问题解决方案,帮助研究者高效完成从命令行计算到图形化展示的全过程。

tech5的博客 591

STM32玩转C++:从Arduino库到HAL库的封装思路(Clion环境配置指南)

本文介绍了如何在STM32开发中使用C++封装HAL库,从Arduino风格过渡到更高效的开发方式。通过Clion环境配置指南和详细的代码示例,展示了如何利用C++的面向对象特性简化GPIO、UART等外设操作,提升代码可读性和开发效率。特别适合嵌入式开发者和从Arduino转向STM32的开发者。

weixin_33737134的博客 336

C++11 在 Windows 环境下的多线程编程指南

C++11Windows多线程开发

威桑的博客 1819

网格自适应_Comsol案例解析17:自适应网格的使用

本期导读自适应网格细化有许多优势,比如可以不用对网格进行全局加密,增加计算精度,又会降低求解自由度。本期仍然以气泡上升案例为例,在两相流模块中使用自适应网格。【打开案例模型】【修改网格】适当让气泡边界加密,注意,曲率因子越小,曲线上的网格越密【添加自适应网格】使用默认的参数即可,这里的误差指示器用的是水平集phi。可以根据具体求解模型选择适当的自变量作为误差指示器。【口诀】默认的一般是最...

weixin_28726871的博客 5682

运用API创建多线程

在Windows平台下创建多线程有两种方式,读者可以使用函数,或者使用函数均可,两者虽然都可以用于创建多线程环境,但还是存在一些差异的,首先函数它是Win32 API的一部分,而是C/C++运行库的一部分,在参数返回值类型方面,返回线程句柄,而返回线程ID,当然这两者在使用上并没有太大的差异,但为了代码更加通用笔者推荐使用后者,因为后者与平台无关性更容易实现跨平台需求。

不定时分享最优质的网络技术与教程,满满的干货。 347

学Simulink--进阶电机控制场景实例:基于Simulink的异步电机定子磁链定向控制(SFOC)仿真

本文详细介绍了基于Simulink的异步电机定子磁链定向控制(SFOC)仿真实现方法。主要内容包括:1) SFOC原理分析,突出其对转子参数不敏感的优势;2) 工业风机驱动系统的完整建模流程,涵盖主电路搭建、坐标变换、磁链观测器设计等关键步骤;3) 核心模块代码实现,包括电压模型磁链观测器和双闭环PI控制器;4) 仿真结果验证了SFOC在转速响应(0.2s)、转矩恢复(0.15s)和参数鲁棒性(转子电阻变化50%时转速波动<2%)方面的优异性能。本文为工程师提供了实用的SFOC系统开发指南,特别适用于

amy_mhd的博客 117

让 RAP 弹窗参数真正变成必填:Excel Upload Action 的两种实现路径

摘要 在RAP应用中,action弹窗参数经常缺乏必填校验,导致用户体验差。本文提出两种解决方案:1)在action实现中手动校验,简单但交互不流畅;2)通过root abstract entity和abstract BDEF建模,利用field(mandatory)实现框架级校验,推荐此长期方案。后者将约束集中到模型层,使UI与执行期校验一致,特别适合ABAP Cloud环境。以Excel导入为例,强制填写备注和明确选择试运行,可避免审计问题,提升系统可靠性。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 200

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

笔者最近在开发基于Internet网上的可视电话过程中碰到了这样一个问题,即在基于In ternet网上的可视电话系统中,同时要进行语音采集、语音编译码、图像采集、图像编译码、语音和图像码流的传输,所有这些工作,都要并行处理。特别是语音信号,如果进行图像编解码时间过长,语音信号得不到服务,通话就有间断;如果图像或语音处理时间过长,而不能及时传输码流数据,通信同样也会中断。这样就要求我们实现一种并行

216

Qt c++ llama.cpp 运行 LLaMA-3-8B 中文对话机器人(多模态输入)

助手:

寒寒豆豆软件公司的博客 223

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

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

抓狂的石头 3658

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

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

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

JamesXing的专栏 1170

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

上一篇: Linux下的多线程编程
下一篇: 不要重新分配被锁定对象的对象引用
java169
博客等级 码龄19年 27粉丝 2568原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值