Windows多线程编程及常见问题

提要:

  • Windows 多线程Helloworld

  • 以Windows代码为例,分析多线程编程中易出现的问题

Windows多线程的Helloworld:

  笔者写过Java多线程的程序(实现Runnable接口,利用Thread类执行),也写过Linux多线程程序(利用pthread)。最近由于另有需要使用Windows多线程,由于Windows
API历来难用,特此记录,以作备忘。

  Helloworld源代码如下:


 1 #include <stdio.h>
2 #include <windows.h>
3
4 #define THREAD_SUM 10
5 int tmp;
6 int sum;
7
8 DWORD WINAPI ThreadProc(LPVOID para){
9 int i;
10 for(i= 0;i<10000;i++)
11 //InterlockedExchangeAdd(&tmp,1);
12 tmp++;
13 printf("%d ",(int)para);
14 sum+=(int)para;
15 return 0;
16 }
17
18 int main(){
19 HANDLE hThread[THREAD_SUM];
20 DWORD id;
21 int i ;
22 for(i= 0;i<THREAD_SUM;i++){
23 hThread[i] = CreateThread(NULL,0,ThreadProc, i,0,&id);
24 // printf("%d ", id);
25 }
26 for(i=0;i<THREAD_SUM;i++){
27 WaitForSingleObject(hThread[i],INFINITE);
28 }
29 printf("\n%d\n%d", tmp, sum);
30 return 0;
31 }

  重点API CreateThread 函数原型如下:


HANDLE CreateThread( //返回线程的句柄
 LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程参数,常置为NULL
 SIZE_T dwStackSize , //初始线程栈大小,可置为0,系统将自行调整
 LPTHREAD_START_ROUTINE lpStartAddress, //线程函数
 LPVOID lpParameter, //传递给线程函数的参数
 DWORD dwCreationFlags,//创建线程标志,可置为NULL
 LPDWORD lpThreadId //线程号
);

  (顺便说下,Windows API
使用匈牙利命名法,即变量名=属性+类型+对象描述,如H代表handle,L代表long,P代表Pointer)

  说明:

  1)其中前两个参数常置为0。

  2)第四个参数为传递给线程的参数,通常为结构体指针,以传递更多的参数。但注意指针指向的内存通常指向的数据不应为局部变量(栈空间变化导致数据异常)。

   此参数也可做为DWORD(双字)型数据传递32位整数数据(本文代码用来传递一个int型数据)

  3)线程号为返回参数,也可置为NULL。

  4)第三个参数即为线程函数,原型如下: 

 DWORD WINAPI ThreadProc(LPVOID para)

  常见问题分析:

  1、如何等待线程。主线程通常需要在各个线程运行结束之后进行一些汇总工作。可以使用WaitForSingleObject(hThread[i],INFINITE)函数等待单个线程结束(类似Linux多线程中的pthread_join)。第一个参数为线程句柄,第二个参数为等待时间(单位毫秒,INFINITE表示一直等待,直到线程结束)。可将代码中WaitForSingleObject这一段注释掉运行,可以发现打印行数减少(甚至可能不输出),且每次数量不同。原理在于主线程结束后进程结束,各个未执行完的线程直接结束。

  2、全局变量共享。代码中各个线程共享全局变量tmp和sum。由于线程执行过程中可能在任意位置中断,因此在线程中更改全局变量的值必须考虑互斥问题。直接进行变量值更新,即如代码中所示。会出现数据异常。本段代码表现为tmp值小于10000*10。对于sum由于更新次数少,线程数也少,数据出现问题几率较小。解决这一问题,可以使用信号量,或者进行加锁等,这里调用InterlockedExchangeAdd,也可以满足要求。该函数可以实现对指定地址的互斥操作。将此段代码注释取消,tmp++代码注释掉,可以发出结果正常。注意此处使用volatile关键字修饰tmp不能解决问题,volatile本身只保证每次使用数据时将从内存读入,忽略寄存器优化,这里仍可存在内在到寄存器存取指令间的中断,从而影响结果。但volatile可用于如下情况:线程中存在循环while(flag),其他线程修改flag标志,使循环结束。

  3、代码运行中还可以发现一个问题,即各个线程printf("%d
",(int)para)结果异常,正常情况下应该输出结果为0~9的乱序,其中互不重复,但实际上,输出结果可能如下:0 0 1 3 3 4 3 4 6 7 2 5
5 8
9(随意截取一次结果)。输出中存在重复项。起初分析以为传递参数时受编译器优化导致传递参数出现问题。但分析汇编可以排除这种可能。细心观察可以发现输出结果数也大于10,可以想到问题出现在printf上。于是忽然想到printf的不可重入。所谓函数不可重入,通常指函数执行过程中不可以发生中断而执行其他的相同函数。这种情况通常由于函数存在全局或者静态变量引用,printf中即存在对stdout的引用。重入导致输出混乱。这里可以观察到sum结果为仍为45(这个结果也可能因为共享全局变量问题发生错误,但本例中更新次数较少,这种情况发生几率很低,也可以用InterlockedExchangeAdd彻底杜绝这种情况的发生),说明参数传递是正确的。

Windows多线程编程及常见问题

时间: 2024-10-13 14:19:22

Windows多线程编程及常见问题的相关文章

Windows多线程编程总结

Windows 多线程编程总结 keyword:多线程 线程同步 线程池 内核对象 1 内核对象 1 .1 内核对象的概念 内核对象是内核分配的一个内存块,这样的内存块是一个数据结构,表示内核对象的各种特征.而且仅仅能由内核来訪问.应用程序若须要訪问内核对象,须要通过操作系统提供的函数来进行,不能直接訪问内核对象( Windows 从安全性方面来考虑的). 内核对象通过 Create* 来创建,返回一个用于标识内核对象的句柄,这些句柄 (而不是内核对象)可在创建进程范围内使用,不可以被传递到其它

【APUE】关于windows多线程编程的学习笔记

保证在某一时刻只有一个线程对数据进行操作的基本方法: (1)关中断:通过关闭时钟中断来停止线程调度(不现实) (2)数学互斥方法:Peterson算法 bakery算法 (3)操作系统提供的互斥方法:临界区.互斥量.信号量等(windows) (4)cpu原子操作:把一些常用的指令设计成了原子指令,在windows上面也被称为原子锁 [APUE]关于windows多线程编程的学习笔记

windows多线程编程(一)(转)

源出处:http://www.cnblogs.com/TenosDoIt/archive/2013/04/15/3022036.html CreateThread:Windows的API函数(SDK函数的标准形式,直截了当的创建方式,任何场合都可以使用),提供操作系统级别的创建线程的操作,且仅限于工作者线程 beginthread beginthreadex:MS对C Runtime库的扩展SDK函数,首先针对C Runtime库做了一些初始化的工作,以保证C Runtime库工作正常,然后,调

windows多线程编程

进程共同实现某个任务或者共享计算机资源, 它们之间存在两种关系: 1.同步关系, 指为了完成任务的进程之间, 因为需要在某些位置协调它们的执行顺序而等待, 传递消息产生的制约关系. 2.互斥关系, 进程间因相互竞争使用独占型资源所产生的制约关系, 如一个进程使用打印机,另一个进程必须等待它使用完后才可使用. 临界资源: 一次仅允许一个进程使(必须互斥使用)的资源, 如独占型硬件资源..... 临界段: 指各进程必须互斥执行的程序段, 该程序段实施对临界资源的操作. 关键区对象为:CRITICAL

Windows下多线程编程(一)

前言 熟练掌握Windows下的多线程编程,能够让我们编写出更规范多线程代码,避免不要的异常.Windows下的多线程编程非常复杂,但是了解一些常用的特性,已经能够满足我们普通多线程对性能及其他要求. 进程与线程 1. 进程的概念 进程就是正在运行的程序.主要包括两部分: • 一个是操作系统用来管理进程的内核对象.内核对象也是系统用来存放关于进程的统计信息的地方. • 另一个是地址空间,它包含所有可执行模块或 D L L模块的代码和数据.它还包含动态内 2. 线程的概念 线程就是描述进程的一条执

Windows下多线程编程

熟练掌握Windows下的多线程编程,能够让我们编写出更规范多线程代码,避免不要的异常.Windows下的多线程编程非常复杂,但是了解一些常用的特性,已经能够满足我们普通多线程对性能及其他要求. 进程与线程 1. 进程的概念 进程就是正在运行的程序.主要包括两部分: ? 一个是操作系统用来管理 ... bbs.chinaacc.com/forum-2-3/topic-5662298.html bbs.chinaacc.com/forum-2-3/topic-5662294.html bbs.ch

C语言使用pthread多线程编程(windows系统)二

我们进行多线程编程,可以有多种选择,可以使用WindowsAPI,如果你在使用GTK,也可以使用GTK实现了的线程库,如果你想让你的程序有更多的移植性你最好是选择POSIX中的Pthread函数库,我的程序是在Linux下写的,所以我使用了Pthread库(是不是很伤心,我知道有不少人期待的是WindowsAPI的,好吧,有机会以后再讲那个,现在先把这一系列专题写完 ^_^)如果你用的是LINUX/UNIX/MacOSX,那么我们已经可以开始了,如果你用的是WINDOWS,那么你需要从网站上下载

【转】Windows的多线程编程,C/C++

在Windows的多线程编程中,创建线程的函数主要有CreateThread和_beginthread(及_beginthreadex). CreateThread 和 ExitThread    使用API函数CreateThread创建线程时,其中的线程函数原型:  DWORD WINAPI ThreadProc(LPVOID lpParameter);在线程函数返回后,其返回值用作调用ExitThread函数的参数(由系统隐式调用).可以使用GetExitCodeThread函数获得该线程

多线程编程简介

http://fanqiang.chinaunix.net/a4/b8/20010811/0905001105.html 1 引言 线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者.传统的Unix 也支持线程的概念,但是在一个进程(process)中只允许有一个线程,这样多线程就意味着多进程.现在,多线程技术已经被许多操作系统所支持,包括 Windows/NT,当然,也包括Linux. 为什么有了进程的概念后,还要再引