VC多线程编程学习笔记(一)

最近两天在学多线程编程,有了一些心得,写下来和大家一起共勉。文中一些部分引用了韩耀旭的文章《多线程编程》http://www.vckbase.com/document/viewdoc/?id=1704和MSDN资料。

一、缘起

工作上要用到串口编程,本来一直是用mscomm控件来进行串口通讯的,后来觉得这个控件功能不灵活,想直接使用api编程,那就不可避免的要使用多线程技术:用一个支线程一直挂在那里监听串口,就不影响主线程的消息循环了。

二、为何要用多线程

有时候需要把程序的运行挂起一段时间,我们会用到

Sleep(5000);

来让程序挂起5秒钟,而这个时候,程序在这5秒钟里就“死”在那里了,不再响应其他任何消息。

或者有时候会用一个

While(1)

这样一个死循环去完成一些监听工作,但这样的话,主界面也会“死”在那里。

这些也许都不是程序员的本意,那我们可以使用多线程技术,把一些耗时的操作放到支线程里去,而不影响主线程的消息循环。

三、相关函数

1.     HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadID );

该函数在其调用进程的进程空间里创建一个新的线程,并返回已建线程的句柄,其中各参数说明如下:

  • lpThreadAttributes:指向一个 SECURITY_ATTRIBUTES 结构的指针,该结构决定了线程的安全属性,一般置为 NULL;
  • dwStackSize:指定了线程的堆栈深度,一般都设置为0;
  • lpStartAddress:表示新线程开始执行时代码所在函数的地址,即线程的起始地址。一般情况为(LPTHREAD_START_ROUTINE)ThreadFunc,ThreadFunc 是线程函数名;
  • lpParameter:指定了线程执行时传送给线程的32位参数,即线程函数的参数;
  • dwCreationFlags:控制线程创建的附加标志,可以取两种值。如果该参数为0,线程在被创建后就会立即开始执行;如果该参数为CREATE_SUSPENDED,则系统产生线程后,该线程处于挂起状态,并不马上执行,直至函数ResumeThread被调用;
  • lpThreadId:该参数返回所创建线程的ID;

如果创建成功则返回线程的句柄,否则返回NULL。

2.     DWORD SuspendThread(HANDLE hThread);

该函数用于挂起指定的线程,如果函数执行成功,则线程的执行被终止。

3.     DWORD ResumeThread(HANDLE hThread);

该函数用于结束线程的挂起状态,执行线程。

4.     VOID ExitThread(DWORD dwExitCode);

该函数用于线程终结自身的执行,主要在线程的执行函数中被调用。其中参数dwExitCode用来设置线程的退出码。

5.     BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);

一般情况下,线程运行结束之后,线程函数正常返回,但是应用程序可以调用TerminateThread强行终止某一线程的执行。各参数含义如下:

  • hThread:将被终结的线程的句柄;
  • dwExitCode:用于指定线程的退出码。

  使用TerminateThread()终止某个线程的执行是不安全的,可能会引起系统不稳定;虽然该函数立即终止线程的执行,但并不释放线程所占用的资源。因此,一般不建议使用该函数。

6.     BOOL PostThreadMessage(DWORD idThread,UINT Msg,WPARAM wParam,LPARAM lParam);

该函数将一条消息放入到指定线程的消息队列中,并且不等到消息被该线程处理时便返回。

  • idThread:将接收消息的线程的ID;
  • Msg:指定用来发送的消息;
  • wParam:同消息有关的字参数;
  • lParam:同消息有关的长参数;

调用该函数时,如果即将接收消息的线程没有创建消息循环,则该函数执行失败。

7.     DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);

  • hHandle为要监视的对象(一般为同步对象,也可以是线程)的句柄;
  • dwMilliseconds为hHandle对象所设置的超时值,单位为毫秒;(有两个特殊值:0,则函数立即返回,若为INFINITE,则函数会一直挂起,直到hHandle所指向的对象变为有信号状态。)

四、两个重要元素

1. Volatile         volatile 修饰符的作用是告诉编译器无需对该变量作任何的优化,即无需将它放到一个寄存器中,并且该值可被外部改变。对于多线程引用的全局变量来说,volatile 是一个非常重要的修饰符。

2. 死锁       多线程编程中,会出现多个线程都需要使用同一个资源,这时,为了保证数据同步,我们可以使用很多种方法,在线程1使用某个资源的时候,锁住它,而此时,线程2也申请使用此资源时,就只能等待,直到线程1释放了这个资源,这是很重要的,但如果不注意,若线程1正在使用资源1,而线程2正在使用资源2,而双方都申请了对方所锁住的资源,则发生了死锁,互相申请不到,结果两个线程都“死”在了那里。

五、一个简单的使用多线程技术的小对话框(使用mfc)

在vc中新建一个对话框程序,布局如图:

1.在对话框的.cpp文件中加入以下两句:

#include "afxmt.h"    //为了使用事件对象CEvent ,所需的头文件 CEvent event1;        //事件对象

2.加入两个全局函数作为两个支线程的入口函数:

UINT proc1(LPARAM lParam) {  CEdit* pEdit = (CEdit*)lParam;  CString str;  int i=1;  while (1)  {   str.Format("第%d次刷新",i);   pEdit->SetWindowText(str);   if (i%5==0)   {    event1.SetEvent();   }   i++;   Sleep(1000);  } }

UINT proc2(LPARAM lParam) {  CEdit* pEdit = (CEdit*)lParam;  CString str;  CTime time;  while (1)  {   WaitForSingleObject(event1.m_hObject,INFINITE);   time = CTime::GetCurrentTime();   str = time.Format("%H : %M : %S");   pEdit->SetWindowText(str);  } }

3.为两个只读的文本框添加控件变量 m_edit1 ,m_edit2

4.为按钮添加单击事件函数:

void CExampleMultiThreadDlg::OnStart() {  HANDLE thread1 = CreateThread(NULL,

0,

(LPTHREAD_START_ROUTINE)proc1,

&m_edit1,

0,

NULL);  HANDLE thread2 = CreateThread(NULL,

0,

(LPTHREAD_START_ROUTINE)proc2,

&m_edit2,

0,

NULL); }

5.运行后,点击开始按钮,两个分线程便开始工作了,线程1会每秒钟刷新一次第一个对话框,每刷新5次,线程2就在第二个对话框中刷新一下当前时间。

程序中用到的CEvent对象是个线程同步对象,用来在线程之间发送信息,我会在下面的学习笔记中阐述用法。

时间: 2024-10-21 12:26:12

VC多线程编程学习笔记(一)的相关文章

多线程编程学习笔记——线程池(一)

接上文 多线程编程学习笔记——线程同步(一) 接上文 多线程编程学习笔记——线程同步(二) 接上文 多线程编程学习笔记——线程同步(三) 创建多线程操作是非常昂贵的,所以每个运行时间非常短的操作,创建多线程进行操作,可能并不能提高效率,反而降低了效率. 如果你有非常多的执行时间非常短的操作,那么适合作用线程池来提高效率,而不是自行创建多线程. 线程池,就是我们先分配一些资源到池子里,当我们需要使用时,则从池子中获取,用完了,再放回池子里. .NET中的线程池是受CLR管理的,TheadTool类

多线程编程学习笔记——线程同步(三)

接上文 多线程编程学习笔记——线程同步(一) 接上文 多线程编程学习笔记——线程同步(二) 七.使用Barrier类 Barrier类用于组织多个线程及时在某个时刻会面,其提供一个回调函数,每次线程调用了SignalAndWait方法后该回调函数就会被执行. 1.代码如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; //

多线程编程学习笔记——async和await(二)

接上文 多线程编程学习笔记——async和await(一) 三.   对连续的异步任务使用await操作符 本示例学习如何阅读有多个await方法方法时,程序的实际流程是怎么样的,理解await的异步调用 . 1.示例代码如下. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Thread

多线程编程学习笔记——async和await(三)

接上文 多线程编程学习笔记——async和await(一) 接上文 多线程编程学习笔记——async和await(二) 五.   处理异步操作中的异常 本示例学习如何在异步函数中处理异常,学习如何对多个并行的异步操作使用await时聚合异常. 1.程序示例代码如下. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;

多线程编程学习笔记——使用并发集合(三)

接上文 多线程编程学习笔记——使用并发集合(一) 接上文 多线程编程学习笔记——使用并发集合(二) 四.   使用ConcurrentBag创建一个可扩展的爬虫 本示例在多个独立的即可生产任务又可消费任务的工作者间如何扩展工作量. 1.程序代码如下. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Sy

多线程编程学习笔记——编写一个异步的HTTP服务器和客户端

接上文 多线程编程学习笔记——使用异步IO 二.   编写一个异步的HTTP服务器和客户端 本节展示了如何编写一个简单的异步HTTP服务器. 1.程序代码如下. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Threading.Ta

Java多线程编程(学习笔记)

一.说明 周末抽空重新学习了下多线程,为了方便以后查阅,写下学习笔记. 有效利用多线程的关键是理解程序是并发执行而不是串行执行的.例如:程序中有两个子系统需要并发执行,这时候需要利用多线程编程. 通过多线程的使用,可以编写出非常高效的程序.但如果创建了太多的线程,程序执行的效率反而会降低. 同时上下文的切换开销也很重要,如果创建太多的线程,CPU花费在上下文的切换时间将对于执行程序的时间. 二.Java多线程编程 概念 在学习多线程时,我们应该首先明白另外一个概念. 进程:是计算机中的程序关于某

多线程编程学习笔记

多线程编程 目录 线程概述 线程的创建 创建线程程序 线程同步 守护线程 线程之间的相互通讯 线程池和java.util.concurrent包 一.概述 1.相关概念 进程(Process):程序(任务)执行的过程,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程,共享内存,共享文件.比如在Windows系统中,一个运行的exe就是一个进程. 线程(Thread):进程中的一个执行流程,一个进程中可以运行多个线程.线程总是属于某个进程,进程中的多个线程共享进程的内存.现在的操作系

VC++网络编程学习笔记

Visual C++网络编程是指用户使用MFC类库(微软基础类库)在VC编译器中,以实现网络应用.用户通过VC编程实现的网络软件可以在网络中不同的计算机之间互传文件,图像等信息.基础知识: 如果用户要进行VC网络编程,则必须首先了解计算机网络通信的基本框架和工作原理.在两台或多台计算机之间进行网络通信时,其通信的双方还必须遵循相同的通信原则好数据格式. 1.OSI七层网络模型OSI网络模型是一个开放式系统互联的参考模型. 发送信息的计算机 接收信息的计算机 7.应用层 7.应用层 表示计算机网络