Windows进程与线程数据结构

《操作系统原理实验》

Windows进程与线程数据结构

主讲:黄伯虎

Windows 体系结构

简化的windows 结构图

Kernel: 由低层次的操作系统功能构成,比如线程调度、中断和异常分发、多处理器同步等。提供了一些例程和基本对象。执行体可以利用这些对象实现更高层次的功能。

Executive: 包含了基本的操作系统服务,包括内存管理、进程和线程管理、安全性、I/O 、网络和跨进程通信等。

Windows基本对象暴露给windows API的执行体对象

执行体对象:

指由执行体的各种组件(比如

进程管理器、内存管理器、

I/O子系统等)所实现的对

象。用户可见。

内核对象:

指由Windows内核实现的一

组更为基本的对象。内核对

象对用户而言是不可见的,

只能在执行体内部被创建和

使用。

Windows进程的组成(从最高抽象层次看)

一个私有的虚拟地址空间

一个可执行的程序:定义了代码和数据,并被映射到进程的虚拟地址空间。

一个已经打开句柄的列表:指向各种资源,比如信号量、文件,该进程的所有线程都可访问这些系统资源。

一个被称为访问令牌的安全环境:标识与该进程关联的用户、安全组和特权

一个被称为进程ID的唯一标识

至少一个执行线程

Windows进程的关键数据结构

执行体进程块(EPROCESS)

执行体进程对象的对象体,包括进程ID、父进程ID、程序名、进程优先级、内存管理信息、设备映像等。

核心进程块(KPROCESS)

内核进程对象的对象体,又称PCB,包括线程调度时需要的信息,如进程状态、线程时间片等。

进程环境块(PEB)

包括用户态代码需要和修改的信息。

Windows环境子系统核心态部件win32k.sys为每个进程建立的进程信息数据结构WIN32KPROCESS

Windows环境子系统进程csrss(用户态)为每个进程建立的进程信息数据结构

Process environment

block

Win32 process block

Handle table

Process address space

System address space

Process block (EPROCESS)

PCB

组成线程的基本部件

一组代表处理器状态的CPU寄存器中的内容

两个栈:一个用于当线程在内核模式下执行的时候,另一个用于线程在用户模式下执行的时候。

一个被称为线程局部存储区(TLS, Thread Local Storage)的私有存储区域:各个子系统、运行库和DLL都会用到该存储区域

一个被称为线程ID的唯一标识符

安全环境

Windows线程的关键数据结构

执行体线程块(ETHREAD)

执行体线程对象的对象体,包括:进程ID、起始执行地址、访问令牌、LPC消息、定时器信息、KTHREAD等。

核心线程块(KTHREAD)

内核线程对象的对象体,包括线程调度信息、同步信息、核心栈信息等。

线程环境块(TEB)

包括用户态代码需要和修改的信息。

Windows环境子系统核心态部件win32k.sys为每个线程建立的线程信息数据结构WIN32THREAD

Windows环境子系统进程csrss(用户态)为每个线程建立的线程信息数据结构

Thread environment

block

Thread block (ETHREAD)

...

Process address space

System address space

TCB Process environment

block

Win32 process block Handle table

Process block (EPROCESS)

PCB

Dispatcher Header

Processor Affinity Kernel Time User Time

Inwwap/Outswap List Entry

Process Spin Lock Resident Kernel Stack Count

Process Base Priority Default Thread Quantum

Process State Thread Seed Disable Boost Flag Process Page Directory

KTHREAD

. . .

KPROCESS

EPEOCESS & KPROCESS

Quota Block

Exit Status Primary Access Token Process ID Parent Process ID

Exception Port Debugger Port

Handle Table

Process Environment Block

Create and Exit Time Next Process Block Image File Name Process Priority Class

Memory Management Information

EPROCESS

Kernel Process Block (or PCB)

Image Base Address Win32 Process Block EPROCESS

PEB

Quota Block

Exit Status Primary Access Token Process ID Parent Process ID

Exception Port Debugger Port

Handle Table

Process Environment Block

Create and Exit Time Next Process Block Image File Name Process Priority Class

Memory Management Information

EPROCESS

Kernel Process Block (or PCB)

Image Base Address Win32 Process Block EPROCESS

PEB

Image base address

Module list

Thread-local storage data

Code page data Critical section time-out Number of heaps Heap size info GDI shared handle table OS version no info Image version info Image process affinity mask

Process heap

Total User Time Total Kernel Time

Thread Scheduling Information

Synchronization Information List of Pending APCs Timer Block and Wait Blocks List of Objects Being Waiting On

System Service Table

TEB

KTHREAD

Thread Local Storage

Kernel Stack Information Dispatcher Header Trap Frame

ETHREAD & KTHREAD

ETHREAD

Create and Exit Time

Process ID

Thread Start Address

Impersonation Information LPC Message Information

EPROCESS

Access Token

KTHREAD Timer Information

Pending I/O Requests

TEB

Exception list

Stack base

Stack limit

Thread ID

Active RPC handle

LastError value Count of owned crit. sect.

Current locale

User32 client info

GDI32 info

OpenGL info

TLS array Subsyst. TIB Fiber info

PEB Winsock data

Windows进程线程模型

参考资料

Books

Mark E. Russinovich and David A. Solomon, Microsoft Windows Internals, 4th Edition, Microsoft Press, 2004.

Chapter 6 -Processes, Thread, and Jobs

实验2-2windows2000 线程同步

实验2 并发与调度 2.2 Windows 2000线程同步 (实验估计时间:120分钟) 背景知识 实验目的 工具/准备工作 实验内容与步骤 背景知识 Windows 2000提供的常用对象可分成三类:核心应用服务、线程同步和线程间通讯。其中,开发人员可以使用线程同步对象来协调线程和进程的工作,以使其共享信息并执行任务。此类对象包括互锁数据、临界段、事件、互斥体和信号等。 多线程编程中关键的一步是保护所有的共享资源,工具主要有互锁函数、临界段和互斥体等;另一个实质性部分是协调线程使其完成应用程序的任务,为此,可利用内核中的事件对象和信号。 在进程内或进程间实现线程同步的最方便的方法是使用事件对象,这一组内核对象允许一个线程对其受信状态进行直接控制 (见表4-1) 。 而互斥体则是另一个可命名且安全的内核对象,其主要目的是引导对共享资源的访问。拥有单一访问资源的线程创建互斥体,所有想要访问该资源的线程应该在实际执行操作之前获得互斥体,而在访问结束时立即释放互斥体,以允许下一个等待线程获得互斥体,然后接着进行下去。 与事件对象类似,互斥体容易创建、打开、使用并清除。利用CreateMutex() API 可创建互斥体,创建时还可以指定一个初始的拥有权标志,通过使用这个标志,只有当线程完成了资源的所有的初始化工作时,才允许创建线程释放互斥体。

为了获得互斥体,首先,想要访问调用的线程可使用OpenMutex() API来获得指向对象的句柄;然后,线程将这个句柄提供给一个等待函数。当内核将互斥体对象发送给等待线程时,就表明该线程获得了互斥体的拥有权。当线程获得拥有权时,线程控制了对共享资源的访问——必须设法尽快地放弃互斥体。放弃共享资源时需要在该对象上调用ReleaseMute() API。然后系统负责将互斥体拥有权传递给下一个等待着的线程(由到达时间决定顺序) 。 实验目的 在本实验中,通过对事件和互斥体对象的了解,来加深对Windows 2000线程同步的理解。 1) 回顾系统进程、线程的有关概念,加深对Windows 2000线程的理解。 2) 了解事件和互斥体对象。 3) 通过分析实验程序,了解管理事件对象的API。 4) 了解在进程中如何使用事件对象。 5) 了解在进程中如何使用互斥体对象。 6) 了解父进程创建子进程的程序设计方法。 工具/准备工作 在开始本实验之前,请回顾教科书的相关内容。 您需要做以下准备: 1) 一台运行Windows 2000 Professional操作系统的计算机。 2) 计算机中需安装Visual C++ 6.0专业版或企业版。 实验内容与步骤 1. 事件对象 2. 互斥体对象 1. 事件对象 清单2-1程序展示了如何在进程间使用事件。父进程启动时,利用CreateEvent() API创建一个命名的、可共享的事件和子进程,然后等待子进程向事件发出信号并终止父进程。在创建时,子进程通过OpenEvent() API打开事件对象,调用SetEvent() API使其转化为已接受信号状态。两个进程在发出信号之后几乎立即终止。 步骤1:登录进入Windows 2000 Professional。 步骤2:在“开始”菜单中单击“程序”-“Microsoft Visual Studio 6.0”–“Microsoft Visual C++ 6.0”命令,进入Visual C++窗口。

Windows多线程程序设计

Windows多线程程序设计- - 1、产生一个线程,只是个框架,没有具体实现。理解::CreateThread函数用法。 #include DWORD WINAPI ThreadFunc(LPVOID); int main() { HANDLE hThread; DWORD dwThreadID; hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(ThreadFunc), NULL, 0, &dwThreadID); ...; return 0; } DWORD WINAPI ThreadFunc(LPVOID lParam) { ...; return 0; } 2、一个真正运转的多线程程序,当你运行它的时候,你会发现(也可能会害怕),自己试试吧。说明了多线程程序是无法预测其行为的,每次运行都会有不同的结果。 #include #include using namespace std; DWORD WINAPI ThreadFunc(LPVOID); int main() { HANDLE hThread; DWORD dwThreadID; // 产生5个线程 for(int i=0; i<5; i++)

{ hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(ThreadFunc), (LPVOID)&i, 0, &dwThreadID); if(dwThreadID) cout << "Thread launched: " << i << endl; } // 必须等待线程结束,以后我们用更好的处理方法 Sleep(5000); return 0; } DWORD WINAPI ThreadFunc(LPVOID lParam) { int n = (int)lParam; for(int i=0; i<3; i++) { cout << n <<","<< n <<","<< n << ","< } return 0; } 3、使用CloseHandle函数来结束线程,应该是“来结束核心对象的”,详细要参见windows 多线程程序设计一书。 修改上面的程序,我们只简单的修改if语句。 if(dwThreadID) { cout << "Thread launched: " << i << endl; CloseHandle(dwThreadID); } 4、GetExitCodeThread函数的用法和用途,它传回的是线程函数的返回值,所以不能用GetExitCodeThread的返回值来判断线程是否结束。 #include #include using namespace std;

一个多线程的windows控制台应用程序

一个多线程的windows控制台应用程序 一、要求: 编写一个单进程、多线程的windows控制台应用程序。 二、平台: Window XP C# 三、内容: 每个进程都有分配给它的一个或多个线程。线程是一个程序的执行部分。 操作系统把极短的一段时间轮流分配给多个线程。时间段的长度依赖于操作系统和处理器。 每个进程都开始一个默认的线程,但是能从它的线程池中创建一个新的线程。 线程是允许进行并行计算的一个抽象概念:在一个线程完成计算任务的同时,另一个线程可以对图像进行更新,两个线程可同时处理同一个进程发出的两个网络请求。 如图所示,选择操作: 1、创建和启动一个线程。在一个进程中同时教和运行两个线程,并且可以不需要停止或者释放一个线程。 相关代码及其解释: public class Threading1:Object { public static void startup() { //创建一个线程数组 Thread[] threads=new Thread[2]; for(int count=0;count

public static void Count() { for(int count=1;count<=9;count++) Console.Write(count+" "); } } 输出结果: 这里通过new方法创建了两个线程,然后使用start()方法来启动线程,两个线程的作用是:两个线程同时从1数到9,并将结果打印出来。 运行上面的程序代码时,可能会在控制台上输出多种不同的结果。从123456789123456789到112233445566778899或121233445566778989在内的各种情况都是可能出现的,输出结果可能与操作系统的调度方式有关。 2、停止线程。当创建一个线程后,可以通过多种属性方法判断该线程是否处于活动状态,启动和停止一个线程等。相关代码及其解释: public class MyAlpha { //下面创建的方法是在线程启动的时候的时候调用 public void Beta() { while(true) { Console.WriteLine("MyAlpha.Beta is running in its own thread."); } } } public class Simple { public static int Stop() { Console.WriteLine("Thread Start/Stop/Join"); MyAlpha TestAlpha=new MyAlpha(); //创建一个线程对象 Thread MyThread=new Thread(new ThreadStart(TestAlpha.Beta)); //开起一个线程 MyThread.Start(); while(!MyThread.IsAlive);

windows 并发的多线程的应用

(1)苹果香蕉问题 #include using namespace std; #include #include int k; HANDLE Apple_;HANDLE Banana_; CRITICAL_SECTION mmutex; DWORD WINAPI Son(LPVOID n) {//HANDLE Apple_; CRITICAL_SECTION mmutex; int i=1; OpenSemaphore(MUTEX_ALL_ACCESS,false,"Apple_"); while(1) { ::WaitForSingleObject(Apple_,INFINITE);//等苹果 cout<<"Son eats"<

Windows下多线程同步机制

多线程同步机制 Critical section(临界区)用来实现“排他性占有”。适用范围是单一进程的各线程之间。它是: ·一个局部性对象,不是一个核心对象。 ·快速而有效率。 ·不能够同时有一个以上的critical section被等待。 ·无法侦测是否已被某个线程放弃。 Mutex Mutex是一个核心对象,可以在不同的线程之间实现“排他性占有”,甚至几十那些现成分属不同进程。它是: ·一个核心对象。 ·如果拥有mutex的那个线程结束,则会产生一个“abandoned”错误信息。 ·可以使用Wait…()等待一个mutex。 ·可以具名,因此可以被其他进程开启。 ·只能被拥有它的那个线程释放(released)。 Semaphore Semaphore被用来追踪有限的资源。它是: ·一个核心对象。 ·没有拥有者。 ·可以具名,因此可以被其他进程开启。 ·可以被任何一个线程释放(released)。 Ev ent Object Ev ent object通常使用于overlapped I/O,或用来设计某些自定义的同步对象。它是: ·一个核心对象。 ·完全在程序掌控之下。 ·适用于设计新的同步对象。 · “要求苏醒”的请求并不会被储存起来,可能会遗失掉。 ·可以具名,因此可以被其他进程开启。 Interlocked Variable 如果Interlocked…()函数被使用于所谓的spin-lock,那么他们只是一种同步机制。所谓spin-lock是一种busy loop,被预期在极短时间内执行,所以有最小的额外负担(overhead)。系统核心偶尔会使用他们。除此之外,interlocked variables主要用于引用技术。他们:·允许对4字节的数值有些基本的同步操作,不需动用到critical section或mutex之类。 ·在SMP(Symmetric Multi-Processors)操作系统中亦可有效运作。 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

在Windows下创建进程和线程的API

利用API在Windows下创建进程和线程 前言: 谈到在Windows创建线程的例子,在网上的很多的参考都是基于MFC的。其实,就操作系统实验这个前提而言,大可不必去碰那个大型的MFC的框架。在Windows命令控制台下可创建进程及线程,做些简单的进程及线程的测试程序。 1、实验准备: 要实验的Windows下的多线程实验,应做如下准备: a) 在新建中选”Win32 Console Application”的An empty project b) 选”工程”的”设置”选项,在”设置”中选择“C/C++”标签,在”Project Option”中,将”MLd”参数改成“MTd”(如图1)。 图1 选项 以上两步对实验成功至关重要,否则,即是代码无误,在连接时同样会出现问题。 2、Windows下进程的创建: Windows的进程和线程模型被描述成”多进程,基于单进程的多线程”。 在创建一个线程时,Windows会做大量的工作---创建一个新的地址空间,为进程分配资源以及创建一个基线程。

CreateProcess函数的原型如下: 虽然有很多参数,不过在现阶段的实验级别,大多数参数只要用默认值即可。 下面要做的关于Windows使用进程的实验,在Linux系统下,可以使用类似: execve(char* cmdName ,char* cmdArgu)的语句从一个程序中去执行其它的程序。 而如果在Windows下,当使用CreateProcess去执行相应的功能时,只要去改变cmdLine中的容即可,其它的参数使用默认值,具体见代码1: 代码1执行的功能是从命令行中启动这个名叫的launch的测试程序,在launch后面应加上保存有需要打开程序路径的文件名: 如在命令行中键入: >launch set.txt 而set.txt中的容为: C:\\WINDOWS\\SYSTEM32\\CALC.EXE C:\\WINDOWS\\SYSTEM32\\NOTEPAD.EXE NEW.TXT C:\\WINDOWS\\SYSTEM32\\CHARMAP.EXE 路径的前半部分为”C:\\WINDOWS\\”,这当然要视你的Windows系统的类型以及系统盘的存放位置而定。如果是NT或2000的机器,则应使用WINNT. /*测试程序1: 示例如使用进程的launch程序(启动程序),通过在命令行中加载相应的命令文件,去按照命令文件中指定的程序路径打开相应的程序去执行*/

Windows多线程及消息队列

1.所谓的worker线程,是指完全不牵扯到图形用户界面(GUI),纯粹做运算的线程。 2.微软的多线程模型: Win32说明文件一再强调线程分为GUI线程和worker线程两种。GUI线程负责建造窗口以及处理主消息循环。Worker负责执行纯粹的运算工作,如重新计算或重新编页等,这些运算工作会导致主线程的消息队列失去反应。一般而言,GUI线程绝不会去做那些不能够马上完成的工作。 GUI线程的定义是:拥有消息队列的线程。任何一个特定窗口的消息总是被产生这一窗口的线程抓到并处理。所有对此窗口的改变也都应该由该线程完成。 如果worker线程也产生了一个窗口,那么就会有一个消息队列随之被产生出来并且附着到此线程身上,于是worker线程摇身一变成了GUI线程。这里的意思是:worker线程不能够产生窗口、对话框、消息框,或任何其他与UI有关的东西。 如果一个worker线程需要输入或输出错误信息,它应该授权给UI线程来做,并且将结果通知给worker线程。 消息队列是一个链表,只有在必要的时候,才有元素产生出来。具体的关于消息队列的数据结构,可以参考相关的windows文档。 3.在Win32中,每一个线程有它自己专属的消息队列。这并不意味着每一个窗口有它自己的消息队列,因为一个线程可以产生许多窗口。如果一个线程停止回应,或是它忙于一段耗时的计算工作,那么由它产生的窗口统统都会停止回应,但系统中的其他窗口还会继续正常工作。 以下是一个非常基本的规则,用来管理Win32中的线程、消息、窗口的互动: 所有传送给某一窗口之消息,将由产生该窗口之线程负责处理。 比方说,使用SetWindowText来更新一个Edit框的内容,其实就是发出了一个WM_SETTEXT 消息给edit窗口函数。推而广之,每一个控件都是一个窗口,都拥有自己的窗口函数。 对窗口所作的一切事情基本上都会被该窗口的窗口函数处理,并因此被产生该窗口的线程处理。当需要发送一个消息时,Windows会自动计算出哪一个线程应该接收到消息(以便确定该消息实体应该挂在在哪一个线程的消息队列中)。同时,windows还会确定线程应该如何被告知有这么一个消息进来。一共有四种可能: (1)如果属于同一线程,使用SendMessage传递消息,则直接调用窗口函数。 (2)如果属于同一线程,使用PostMessage传递消息,则把消息放在消息队列中然后立即返回。(3)如果不属于同一线程,使用SendMessage传递消息,则切换到新线程中并调用窗口函数。在该窗口函数结束之前,SendMessage不会返回。 (4)PostMessage立刻返回,消息则被放到另一线程的消息队列中。 当我send一个消息给另一线程掌握的窗口时,系统必须做一次context switch,切换到另一线程去,调用该窗口函数,然后再做一次contex t switch切换回来,相对一般的函数调用而言,期间的额外负担较大。如果在MDI中,为每个子窗口分配一个线程,那么该子窗口的所有资源——包括画刷,DC,调色板等等都属于线程的资源。此时为线程做context switch时会代价很大。

22进程、线程与并行计算(windows 编程技术)

第22章 进程、线程与并行计算 进程是正在运行的程序,线程是轻量级的进程。多任务的并发执行会用到多线程(multithreading ),而CPU 的多核(mult-core )化又将原来只在巨型机和计算机集群中才使用的并行计算带入普通PC 应用的多核程序设计中。 本章先介绍进程与线程的概念和编程,再给出并行计算的基本概念和内容。下一章讨论基于多核CPU 的并行计算的若干具体编程接口和方法。 22.1 进程与线程 进程(process )是执行中的程序,线程(thread )是一种轻量级的进程。 22.1.1 进程与多任务 现代的操作系统都是多任务(multitask )的,即可同时运行多个程序。进程(process )是位于内存中正被CPU 运行的可执行程序实例,参见图22-1。 图22-1 程序与进程 目前的主流计算机采用的都是冯·诺依曼(John von Neumann )体系结构——存储程序计算模型。程序(program )是在内存中顺序存储并以线性模式在CPU 中串行执行的指令序列。对于传统的单核CPU 计算机,多任务操作系统的实现是通过CPU 分时(time-sharing )和程序并发(concurrency )完成的。即在一个时间段内,操作系统将CPU 分配给不同的程序,虽然每一时刻只有一个程序在CPU 中运行,但是由于CPU 的速度非常快,在很短的时间段中可在多个进程间进行多次切换,所以用户的感觉就像多个程序在同时执行,我们称之为多任务的并发。 22.1.2 进程与线程 程序一般包括代码段、数据段和堆栈,对具有GUI (Graphical User Interfaces ,图形用户界面)的程序还包含资源段。进程(process )是应用程序的执行实例,即正在被执行的程进程(内存中) 可执行文件(盘上) 运行

Windows进程与线程数据结构

《操作系统原理实验》 Windows进程与线程数据结构 主讲:黄伯虎

Windows 体系结构 简化的windows 结构图 Kernel: 由低层次的操作系统功能构成,比如线程调度、中断和异常分发、多处理器同步等。提供了一些例程和基本对象。执行体可以利用这些对象实现更高层次的功能。 Executive: 包含了基本的操作系统服务,包括内存管理、进程和线程管理、安全性、I/O 、网络和跨进程通信等。

Windows基本对象暴露给windows API的执行体对象 执行体对象: 指由执行体的各种组件(比如 进程管理器、内存管理器、 I/O子系统等)所实现的对 象。用户可见。 内核对象: 指由Windows内核实现的一 组更为基本的对象。内核对 象对用户而言是不可见的, 只能在执行体内部被创建和 使用。

Windows进程的组成(从最高抽象层次看) 一个私有的虚拟地址空间 一个可执行的程序:定义了代码和数据,并被映射到进程的虚拟地址空间。 一个已经打开句柄的列表:指向各种资源,比如信号量、文件,该进程的所有线程都可访问这些系统资源。 一个被称为访问令牌的安全环境:标识与该进程关联的用户、安全组和特权 一个被称为进程ID的唯一标识 至少一个执行线程

Windows进程的关键数据结构 执行体进程块(EPROCESS) 执行体进程对象的对象体,包括进程ID、父进程ID、程序名、进程优先级、内存管理信息、设备映像等。 核心进程块(KPROCESS) 内核进程对象的对象体,又称PCB,包括线程调度时需要的信息,如进程状态、线程时间片等。 进程环境块(PEB) 包括用户态代码需要和修改的信息。 Windows环境子系统核心态部件win32k.sys为每个进程建立的进程信息数据结构WIN32KPROCESS Windows环境子系统进程csrss(用户态)为每个进程建立的进程信息数据结构

Windows多线程编程_C语言

Windows多线程编程-C语言 先上代码: #include #include// for HANDLE #include// for _beginthread() #include unsigned__stdcall thread(void * i) //子线程入口函数 { int * k = (int *)i; printf("这是子线程%d\n", *k); return 1; // the thread exit code } int main() { HANDLE hth1;//子线程句柄 unsigned Thread1ID;//子线程ID int i = 1;//子线程入口函数参数 //创建子线程 hth1 = (HANDLE)_beginthreadex(NULL, // security,安全属性 0, // stack size thread, //子线程入口函数 &i, // arg list 入口函数参数地址 CREATE_SUSPENDED, //先挂起该线程 &Thread1ID);//线程标识符 if (hth1 == 0)//如果返回的hth1的值为0 则表示创建子线程失败 printf("Failed to create thread 1\n"); DWORD ExitCode; //线程退出码 GetExitCodeThread(hth1, &ExitCode); //获取线程退出码,因为刚刚创建,所以 //肯定还未退出,此时的退出码应为259,表示STILL_ACTIVE printf("initial thread 1 exit code = %u线程未退出\n", ExitCode); ResumeThread(hth1); // 激活线程 WaitForSingleObject(hth1, INFINITE);//等待线程结束 GetExitCodeThread(hth1, &ExitCode);//获取线程退出码 printf("thread 1 exited with code %u,its pid=%u,its handle=%d\n", ExitCode, Thread1ID,hth1);

Windows中线程间同步的方法

Windows中线程间同步的方法主要有:事件(Event)、临界区(Critical Section)、互斥量(Mutex)和信号灯(Semaphore)。 1)使用事件对象进行线程间同步: 在使用CreateEvent函数创建事件对象时,将bManualReset参数设置为FALSE,然后在需要独占操作的代码前面加上一个WaitForSingleObject函数,后面加上一个SetEvent即可。 由于bManualReset参数为FALSE,这样当某个线程等待到Event后,Event对象的状态马上就变为复位状态,这样其他线程执行到WaitForSingleObject时就全部处于等待中了,当活动的线程操作完毕后,执行SetEvent函数,Event对象的状态才恢复到置位,这样其他等待的线程才会有一个能继续操作。 SetEvent函数原型如下: HANDLE WINAPI CreateEvent( __in_opt LPSECURITY_ATTRIBUTES lpEventAttributes, //安全描述符 __in BOOL bManualReset, //指定事件对象是否需要手动复位 __in BOOL bInitialState, //指定事件对象创建时的初始状态,为TRUE表示初始状态是置位状态; //为FALSE表示初始状态是复位状态 __in_opt LPCTSTR lpName //指定事件对象名称 ); 当一个事件被创建后,程序就可以通过SetEvent和ResetEvent函数来设置事件的状态:BOOL WINAPI SetEvent( __in HANDLE hEvent ); BOOL WINAPI ResetEvent(

当前流行的Windows操作系统能同时运行几个程序独立运行

当前流行的Windows操作系统能同时运行几个程序(独立运行的程序又称之为进程),对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程,线程提供了多任务处理的能力。用进程和线程的观点来研究软件是当今普遍采用的方法,进程和线程的概念的出现,对提高软件的并行性有着重要的意义。现在的大型应用软件无一不是多线程多任务处理,单线程的软件是不可想象的。因此掌握多线程多任务设计方法对每个程序员都是必需要掌握的。本实例针对多线程技术在应用中经常遇到的问题,如线程间的通信、同步等,分别进行探讨,并利用多线程技术进行线程之间的通信,实现了数字的简单排序。 一、实现方法 1、理解线程 要讲解线程,不得不说一下进程,进程是应用程序的执行实例,每个进程是由私有的虚拟地址空间、代码、数据和其它系统资源组成。进程在运行时创建的资源随着进程的终止而死亡。线程的基本思想很简单,它是一个独立的执行流,是进程内部的一个独立的执行单元,相当于一个子程序,它对应于Visual C++中的CwinThread类对象。单独一个执行程序运行时,缺省地包含的一个主线程,主线程以函数地址的形式出现,提供程序的启动点,如main ()或WinMain()函数等。当主线程终止时,进程也随之终止。根据实际需要,应用程序可以分解成许多独立执行的线程,每个线程并行的运行在同一进程中。 一个进程中的所有线程都在该进程的虚拟地址空间中,使用该进程的全局变量和系统资源。操作系统给每个线程分配不同的CPU时间片,在某一个时刻,CPU只执行一个时间片内的线程,多个时间片中的相应线程在CPU内轮流执行,由于每个时间片时间很短,所以对用户来说,仿佛各个线程在计算机中是并行处理的。操作系统是根据线程的优先级来安排CPU 的时间,优先级高的线程优先运行,优先级低的线程则继续等待。 线程被分为两种:用户界面线程和工作线程(又称为后台线程)。用户界面线程通常用来处理用户的输入并响应各种事件和消息,其实,应用程序的主执行线程CWinAPP对象就是一个用户界面线程,当应用程序启动时自动创建和启动,同样它的终止也意味着该程序的结束,进程终止。工作线程用来执行程序的后台处理任务,比如计算、调度、对串口的读写操作等,它和用户界面线程的区别是它不用从CWinThread类派生来创建,对它来说最重要的是如何实现工作线程任务的运行控制函数。工作线程和用户界面线程启动时要调用同一个函数的不同版本;最后需要读者明白的是,一个进程中的所有线程共享它们父进程的变量,但同时每个线程可以拥有自己的变量。

Windows环境下的多线程编程

Windows环境下的多线程编程浅讲这是一个简单的关于多线程编程的指南,任何初学者都能在这里找到关于编写简单多线程程序的方法。如果需要更多的关于这方面的知识可以参考MSDN或本文最后列出的参考文献。本文将以C语言作为编程语言,在Lcc-Win32编译器下进行编译调试,使用MS-Visual Studio的人可以参考其相关的使用手册。没有C语言基础的朋友可以找一本简单的教程看一下,相信会是有帮助的。 首先解释什么是线程,谈到这个就不得不说一下什么是进程,这是两个相关的概念。为了方便理解,我推荐大家下载并使用一下flashget(网际快车)这个下载软件,当然迅雷也行,只是不如flashget这么直观了。O.K.言归正传,当我们打开flashget这个软件时,就是启动了一个进程。这个进程就指flashget这个软件的运行状态。当我们用它来下载时都是在整个flashget软件里操作,所以可以把单个进程看作整个程序的最外层。然后我们可以看到,在用flashget下载时可以选择同时使用多少线程来下载的选项(这里介绍个小技巧,用超级兔子可以优化这里的选项,将最大的线程数限制从10改为30),这个选项就包含了我所要讲的线程的概念。当同时用比如5根线程来下载的话,flashget就会同时使用5个独立的下载程序来下载文件(各个线程通过某种方式通信,以确定各自所要下载的部分,关于线程间的通信在后面介绍)。所以线程可以看作是在进程框架下独立运行的与进程功能相关的程序。如果将windows看作进程的话,那么里面跑得QQ啊,MSN什么的都可以看作是windows的线程了。当然进程本身也可以看作是线程,只是凌驾于其它线程之上的线程罢了。另外比如我们使用浩方对战平台来网上对战,当用浩方启动魔兽时,由于运行的是两个不同的程序,那就是多进程编程了,这要比多线程复杂点,但也是复杂的有限,有兴趣的人可以参考MSDN上的相关资料。 最后声明一下,文中的例子都没有过多的注释,不过我都使用了最简单的例子(至少我个人已经想不出更简单的了),如果读者对理解这里的程序上有困难,那么我的建议是这篇文章已经不适合你了,你应该看一些更基础的书,比如《小学生基础算数》^_^。 为了使对多线程编程有个更感性的认识,这里先给出一个简单的多线程程序。

基于Windows多线程环境下的串口通信

第46卷 第3期 武汉大学学报(自然科学版) V ol.46 N o.3 2000年6月 J.W uhan U niv.(N at.Sci.Ed.) June,2000,373~375 文章编号:0253-9888(2000)03-0373-03 基于Windows多线程环境下的串口通信 陈淑珍,石 波 (武汉大学电子信息学学院,武汉430072) 摘 要:根据串口通信的基本原理,结合W indow s环境下的多任务并发机制,采用Window s的多线程技术来实现串口动态实时通信.有效地解决了在串口通信中的实时响应问题,降低了数据的丢失率,提高了系统的可靠性.同时提出了在Window s环境下实现串口通信的一般方法和步骤.实践证明,这种结合多线程技术的串口通信方法具有很强的实用性. 关 键 词:多线程;串行通信;实时查询 中图分类号:T P311.11 文献标识码:A 在实际的工程应用中,应用程序经常需要具备与外围设备进行通信的能力.在与串口,调制解调器,或是通过电话线进行通信的应用程序中,异步串行通信是一种重要的通信手段.在单任务的操作系统中,应用程序不能处理通信过程中的突发和并发事件,这种缺陷会引起数据丢失和不可靠性.而Window s基于线程的多任务并发机制使得应用程序能同时执行不同的任务,达到了降低数据丢失率,提高系统可靠性的目的. 1 串口通信的原理和机制 不论何种通信,背后都需要一个通信协议的支持.串口通信大多采用了美国电子工业协会(EIA)于1969年制定的RS-232标准[1].RS-232标准规定了数据终端设备和数据通信设备之间的连接和通信规则.该协议运用RTS(Request to Send)和(Clear to Send)信号来实现串口和外围设备的硬件“握手”,从而建立通信双方的连接和应答.在通信的连接和应答完成以后,双方就可以在误差允许范围内进行串行通信. 由于Window s是一个基于消息驱动的操作系统,它的很多消息是从硬件反馈过来的.Window s 不允许程序开发人员直接和硬件打交道,在串口通信方面提供了一组API系统函数来管理串口,这对减低编程工作量,提高系统的稳定性和安全性都是很有好处的. 在Win9x操作系统中,对串行通讯设备的操作如同文件的操作一样:串行通讯设备的打开、读写和关闭等操作均与文件操作相同,这和以前Win3x中的通信方式不同.由于w in9x系统中取消了串行通讯中的特定消息WM_COMM NOTIFY(外围通讯设备一有相应事件发生,该消息就会被传送),使得应用程序工作于“事件驱动”方式时,应创建专用的线程来监视有关的串行通讯设备. ●打开和关闭串口.通信会话以调用CreateFile函数开始,为读、写操作打开串口.为实现串口的排他性访问,共享标志应设置成false,创建标志应为o pen_exiting,模板句柄应为null,同时返回串口句柄. 通信会话通过调用CloseHandle函数来关闭串口占用的内存句柄,释放相应的串口资源. ●初始化和配置串口.一旦串口处于打开状态,Window s就可以给串口分配接受和发送缓冲区.缓冲区的大小既可以缺省,也可以指定(调用SetupComm函数). 配置串口需要设置串口通讯中特定事件的掩码(调用SetCom mM ask),只要串口中出现特定的消 收稿日期:2000-02-22  基金项目:九五国家重点科技攻关项目(204980340) 作者简介:陈淑珍(1946-),女,教授,现从事计算机网络与多媒体研究.

Windows多线程编程

Windows多线程编程 作者:韩耀旭 多线程编程之一——问题提出 (2) 一、问题的提出 (2) 二、多线程概述 (2) 三、Win32API对多线程编程的支持 (3) 四、Win32API多线程编程例程 (4) 例程1MultiThread1 (4) 例程2MultiThread2 (5) 例程3MultiThread3 (6) 例程4MultiThread4 (9) 多线程编程之二——MFC中的多线程开发 (11) 五、MFC对多线程编程的支持 (11) 六、MFC多线程编程实例 (12) 例程5MultiThread5 (12) 例程6MultiThread6 (15) 多线程编程之三——线程间通讯 (19) 七、线程间通讯 (19) 例程7MultiThread7 (19) 多线程编程之四——线程的同步 (23) 八、线程的同步 (23) 例程8MultiThread8 (24) 例程9MultiThread9 (26) 例程10MultiThread10 (29) 注:参考自https://www.360docs.net/doc/428590863.html,/space-290696-do-blog-id-13861.html

多线程编程之一——问题提出 下载源代码 https://www.360docs.net/doc/428590863.html,/link.php?url=https://www.360docs.net/doc/428590863.html,%2Fcode%2Fdowncode.asp%3Fid%3D29 73 一、问题的提出 编写一个耗时的单线程程序: 新建一个基于对话框的应用程序SingleThread,在主对话框IDD_SINGLETHREAD_DIALOG添加一个按钮,ID为IDC_SLEEP_SIX_SECOND,标题为“延时6秒”,添加按钮的响应函数,代码如下: void CSingleThreadDlg::OnSleepSixSecond() { Sleep(6000);//延时6秒 } 编译并运行应用程序,单击“延时6秒”按钮,你就会发现在这6秒期间程序就象“死机”一样,不在响应其它消息。为了更好地处理这种耗时的操作,我们有必要学习——多线程编程。 二、多线程概述 进程和线程都是操作系统的概念。进程是应用程序的执行实例,每个进程是由私有的虚拟地址空间、代码、数据和其它各种系统资源组成,进程在运行过程中创建的资源随着进程的终止而被销毁,所使用的系统资源在进程终止时被释放或关闭。 线程是进程内部的一个执行单元。系统创建好进程后,实际上就启动执行了该进程的主执行线程,主执行线程以函数地址形式,比如说main或WinMain函数,将程序的启动点提供给Windows系统。主执行线程终止了,进程也就随之终止。 每一个进程至少有一个主执行线程,它无需由用户去主动创建,是由系统自动创建的。用户根据需要在应用程序中创建其它线程,多个线程并发地运行于同一个进程中。一个进程中的所有线程都在该进程的虚拟地址空间中,共同使用这些虚拟地址空间、全局变量和系统资源,所以线程间的通讯非常方便,多线程技术的应用也较为广泛。 多线程可以实现并行处理,避免了某项任务长时间占用CPU时间。要说明的一点是,目前大多数的计算机都是单处理器(CPU)的,为了运行所有这些线程,操作系统为每个独立线程安排一些CPU时间,操作系统以轮换方式向线程提供时间片,这就给人一种假象,好象这些线程都在同时运行。由此可见,如果两个非常活跃的线程为了抢夺对CPU的控制权,在线程切换时会消耗很多的CPU资源,反而会降低系统的性能。这一点在多线程编程时应该注意。 Win32SDK函数支持进行多线程的程序设计,并提供了操作系统原理中的各种同步、互斥和临界区等操作。Visual C++6.0中,使用MFC类库也实现了多线程的程序设计,使得多线程编程更加方便。

windows环境下C语言多线程实现网络编程,多人聊天室,

/*********************** 服务器 ************************/ #include "stdafx.h" #include #include #include #include "my_typedef.h" #pragma comment(lib,"ws2_32.lib") #define L_MAX (255) #define C_MAX (100) DWORD WINAPI ThreadProc( /* 线程函数*/ LPVOID lpParam ); HANDLE tThread_Client[C_MAX] = {NULL}; LNode *pHead; int main(int argc, char* argv[]) { //初始化WSA WORD sockVersion = MAKEWORD(2,2); WSADATA wsaData; if(WSAStartup(sockVersion, &wsaData)!=0) { return 0; } //创建套接字 SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(slisten == INV ALID_SOCKET) { printf("socket error !"); return 0; } //绑定IP和端口 sockaddr_in sin;

sin.sin_family = AF_INET; sin.sin_port = htons(8888); sin.sin_addr.S_un.S_addr = INADDR_ANY; if(bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR) { printf("bind error !"); } //开始监听 if(listen(slisten, C_MAX) == SOCKET_ERROR) { printf("listen error !"); return 0; } sockaddr_in remoteAddr; int nAddrlen = sizeof(remoteAddr); SOCKET sClient = NULL; SOCKET Socket_Sclient = NULL; /* 创建一个链表存放已经连接的客户端*/ pHead = (LNode*)malloc(sizeof(LNode)); pHead->pNext = NULL; pHead->sClient = NULL; /*循环等待客户端连接,并在连接后创建一个SOCKET和线程*/ while (true) { int i = 0; printf("等待连接...\n"); sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen); if(sClient == INV ALID_SOCKET) { printf("accept error !"); continue; } else { Socket_Sclient = sClient; add_Element(pHead,sClient); tThread_Client[i] = CreateThread(NULL, 0, ThreadProc,(LPVOID)Socket_Sclient,

相关文档
最新文档