多线程详解

1.   进程与线程有那些区别和联系?
  每个进程至少需要一个线程。
        进程由两部分构成:进程内核对象,地址空间。线程也由两部分组成:线程内核对象,操作系统用它来对线程实施管理。线程堆栈,用于维护线程在执行代码时需要的所有函数参数和局部变量。
        进程是不活泼的。进程从来不执行任何东西,它只是线程的容器。线程总是在某个进程环境中创建的,而且它的整个寿命期都在该进程中。
        如果在单进程环境中,有多个线程正在运行,那么这些线程将共享单个地址空间。这些线程能够执行相同的代码,对相同的数据进行操作。这些线程还能共享内核对象句柄,因为句柄表依赖于每个进程而不是每个线程存在。
  进程使用的系统资源比线程多得多。实际上,线程只有一个内核对象和一个堆栈,保留的记录很少,因此需要很少的内存。因此始终都应该设法用增加线程来解决编程问题,避免创建新的进程。但是许多程序设计用多个进程来实现会更好些。

2.   如何使用_beginthreadex函数?
        使用方法与CreateThread函数相同,只是调用参数类型需要转换。

3.   如何使用CreateThread函数?
        当CreateThread被调用时,系统创建一个线程内核对象。该线程内核对象不是线程本身,而是操作系统用来管理线程的较小的数据结构。使用时应当注意在不需要对线程内核进行访问后调用CloseHandle函数关闭线程句柄。因为CreateThread函数中使用某些C/C++运行期库函数时会有内存泄漏,所以应当尽量避免使用。
参数含义:
lpThreadAttributes     如果传递NULL该线程使用默认安全属性。如果希望所有的子进程能够继承该线程对象的句柄,必须将它的bInheritHandle成员被初始化为TRUE。
dwStackSize     设定线程堆栈的地址空间。如果非0,函数将所有的存储器保留并分配给线程的堆栈。如果是0,CreateThread就保留一个区域,并且将链接程序嵌入.exe文件的/STACK链接程序开关信息指明的存储器容量分配给线程堆栈。
lpStartAddress     线程函数的地址。
lpParameter     传递给线程函数的参数。
dwCreationFlags     如果是0,线程创建后立即进行调度。如果是CREATE_SUSPENDED,系统对它进行初始化后暂停该线程的运行。
LpThreadId     用来存放系统分配给新线程的ID。

4.   如何终止线程的运行?
(1)       线程函数返回(最好使用这种方法)。
这是确保所有线程资源被正确地清除的唯一办法。如果线程能够返回,就可以确保下列事项的实现:
  在线程函数中创建的所有C++对象均将通过它们的撤消函数正确地撤消。操作系统将正确地释放线程堆栈使用的内存。
系统将线程的退出代码设置为线程函数的返回值。系统将递减线程内核对象的使用计数。
(2)       调用ExitThread函数(最好不要使用这种方法)。
该函数将终止线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。但是,C++资源(如C++类对象)将不被撤消。
(3)         调用TerminateThread函数(应该避免使用这种方法)。
TerminateThread能撤消任何线程。线程的内核对象的使用计数也被递减。TerminateThread函数是异步运行的函数。如果要确切地知道该线程已经终止运行,必须调用WaitForSingleObject或者类似的函数。当使用返回或调用ExitThread的方法撤消线程时,该线程的内存堆栈也被撤消。但是,如果使用TerminateThread,那么在拥有线程的进程终止运行之前,系统不撤消该线程的堆栈。
(4)         包含线程的进程终止运行(应该避免使用这种方法)。由于整个进程已经被关闭,进程使用的所有资源肯定已被清除。就像从每个剩余的线程调用TerminateThread一样。这意味着正确的应用程序清除没有发生,即C++对象撤消函数没有被调用,数据没有转至磁盘等等。一旦线程不再运行,系统中就没有别的线程能够处理该线程的句柄。然而别的线程可以调GetExitcodeThread来检查由hThread标识的线程是否已经终止运行。如果它已经终止运行,则确定它的退出代码。

5.   为什么不要使用_beginthread函数和_endthread函数?
  与_beginthreadex函数相比参数少,限制多。无法创建暂停的线程,无法取得线程ID。    Endthread函数无参数,线程退出代码必须为0。还有_endthread函数内部关闭了线程的句柄,一旦退出将不能正确访问线程句柄。

6.   如何对进程或线程的内核进行引用?
HANDLE   GetCurrentProcess(     );
HANDLE   GetCurrentThread(     );
这两个函数都能返回调用线程的进程的伪句柄或线程内核对象的伪句柄。伪句柄只能在当前的进程或线程中使用,在其它线程或进程将不能访问。函数并不在创建进程的句柄表中创建新句柄。调用这些函数对进程或线程内核对象的使用计数没有任何影响。如果调用CloseHandle,将伪句柄作为参数来传递,那么CloseHandle就会忽略该函数的调用并返回FALSE。
DWORD   GetCurrentProcessId(     );
DWORD   GetCurrentThreadId(     );
这两个函数使得线程能够查询它的进程的唯一ID或它自己的唯一ID。

7.   如何将伪句柄转换为实句柄?
HANDLE   hProcessFalse   =   NULL;
HANDLE   hProcessTrue   =   NULL;
HANDLE   hThreadFalse   =   NULL;
HANDLE   hThreadTrue   =   NULL;

hProcessFalse   =   GetCurrentProcess(     );
hThreadFalse   =   GetCurrentThread(     );
取得线程实句柄:
DuplicateHandle(   hProcessFalse,   hThreadFalse,   hProcessFalse,   &hThreadTrue,   0,   FALSE,   DUPLICATE_SAME_ACCESS   );
取得进程实句柄:
DuplicateHandle(   hProcessFalse,   hProcessFalse,   hProcessFalse,   &hProcessTrue,   0,   FALSE,   DUPLICATE_SAME_ACCESS   );
由于DuplicateHandle会递增特定对象的使用计数,因此当完成对复制对象句柄的使用时,应该将目标句柄传递给CloseHandle,从而递减对象的使用计数。

8.   在一个进程中可创建线程的最大数是得多少?
线程的最大数取决于该系统的可用虚拟内存的大小。默认每个线程最多可拥有至多1MB大小的栈的空间。所以,至多可创建2028个线程。如果减少默认堆栈的大小,则可以创建更多的线程。

线程的调度、优先级和亲缘性
9.   如何暂停和恢复线程的运行?
      线程内核对象的内部有一个值指明线程的暂停计数。当调用CreateProcess或CreateThread函数时,就创建了线程的内核对象,并且它的暂停计数被初始化为1。因为线程的初始化需要时间,不能在系统做好充分的准备之前就开始执行线程。线程完全初始化好了之后,CreateProcess或CreateThread要查看是否已经传递了CREATE_SUSPENDED标志。如果已经传递了这个标志,那么这些函数就返回,同时新线程处于暂停状态。如果尚未传递该标志,那么该函数将线程的暂停计数递减为0。当线程的暂停计数是0的时候,除非线程正在等待其他某种事情的发生,否则该线程就处于可调度状态。在暂停状态中创建一个线程,就能够在线程有机会执行任何代码之前改变线程的运行环境(如优先级)。一旦改变了线程的环境,必须使线程成为可调度线程。方法如下:
hThread   =   CreatThread(   ……,CREATE_SUSPENDED,……   );

bCreate   =   CreatProcess(   ……,CREATE_SUSPENDED,……,pProcInfo   );
if(   bCreate   !=   FALSE   )
{
  hThread   =   pProcInfo.hThread;
}
……
……
……
ResumeThread(   hThread   );
CloseHandle(   hThread   );
ResumeThread成功,它将返回线程的前一个暂停计数,否则返回0xFFFFFFFF。

单个线程可以暂停若干次。如果一个线程暂停了3次,它必须恢复3次。创建线程时,除了使用CREATE_SUSPENDED外,也可以调用SuspendThread函数来暂停线程的运行。任何线程都可以调用该函数来暂停另一个线程的运行(只要拥有线程的句柄)。线程可以自行暂停运行,但是不能自行恢复运行。与ResumeThread一样,SuspendThread返回的是线程的前一个暂停计数。线程暂停的最多次数可以是MAXIMUM_SUSPEND_COUNT次。SuspendThread与内核方式的执行是异步进行的,但是在线程恢复运行之前,不会发生用户方式的执行。调用SuspendThread时必须小心,因为不知道暂停线程运行时它在进行什么操作。只有确切知道目标线程是什么(或者目标线程正在做什么),并且采取强有力的措施来避免因暂停线程的运行而带来的问题或死锁状态,SuspendThread才是安全的。

10.   是否可以暂停和恢复进程的运行?
        对于Windows来说,不存在暂停或恢复进程的概念,因为进程从来不会被安排获得CPU时间。不过Windows确实允许一个进程暂停另一个进程中的所有线程的运行,但是从事暂停操作的进程必须是个调试程序。特别是,进程必须调用WaitForDebugEvent和ContinueDebugEvent之类的函数。由于竞争的原因,Windows没有提供其他方法来暂停进程中所有线程的运行。

11.   如何使用sleep函数?
        系统将在大约的指定毫秒数内使线程不可调度。Windows不是个实时操作系统。虽然线程可能在规定的时间被唤醒,但是它能否做到,取决于系统中还有什么操作正在进行。
  可以调用Sleep,并且为dwMilliseconds参数传递INFINITE。这将告诉系统永远不要调度该线程。这不是一件值得去做的事情。最好是让线程退出,并还原它的堆栈和内核对象。可以将0传递给Sleep。这将告诉系统,调用线程将释放剩余的时间片,并迫使系统调度另一个线程。但是,系统可以对刚刚调用Sleep的线程重新调度。如果不存在多个拥有相同优先级的可调度线程,就会出现这种情况。

12.   如何转换到另一个线程?
        系统提供了SwitchToThread函数。当调用这个函数的时候,系统要查看是否存在一个迫切需要CPU时间的线程。如果没有线程迫切需要CPU时间,SwitchToThread就会立即返回。如果存在一个迫切需要CPU时间的线程,SwitchToThread就对该线程进行调度(该线程的优先级可能低于调用SwitchToThread的线程)。这个迫切需要CPU时间的线程可以运行一个时间段,然后系统调度程序照常运行。该函数允许一个需要资源的线程强制另一个优先级较低、而目前却拥有该资源的线程放弃该资源。如果调用SwitchToThread函数时没有其他线程能够运行,那么该函数返回FALSE,否则返回一个非0值。调用SwitchToThread与调用Sleep是相似的。差别是SwitchToThread允许优先级较低的线程运行;而即使有低优先级线程迫切需要CPU时间,Sleep也能够立即对调用线程重新进行调度。

13.   如何取得线程运行的时间?
(1)       简单取得线程大概运行时间:
DWORD   dwStartTime   =   0;
DWORD   dwEndTime   =   0;
DWORD   dwRunTime   =   0;
dwStartTime   =   GetTickCount(     );
……
……
……
dwEndTime   =   GetTickCount(     );
dwRunTime   =   dwEndTime   –   dwStartTime;
(2)         调用GetThreadTimes的函数:
参数含义:
hThread   线程句柄
lpCreationTime   创建时间:英国格林威治时间
lpExitTime   退出时间:英国格林威治时间,如果线程仍然在运行,退出时间则未定义
lpKernelTime   内核时间:指明线程执行操作系统代码已经经过了多少个100ns的CPU时间
lpUserTime   用户时间:指明线程执行应用程序代码已经经过了多少个100ns的CPU时间
GetProcessTimes是个类似GetThreadTimes的函数,适用于进程中的所有线程(甚至是已经终止运行的线程)。返回的内核时间是所有进程的线程在内核代码中经过的全部时间的总和。GetThreadTimes和GetProcessTimes这两个函数在Windows98中不起作用。在Windows98中,没有一个可靠的机制可供应用程序来确定线程或进程已经使用了多少CPU时间。

14.   进程的优先级类有哪些?
优先级类   标识符   描述
实时           REALTIME_PRIORITY_CLASS   立即对事件作出响应,执行关键时间的任务。会抢先于操作系统组件之前运行。
高            HIGH_PRIORITY_CLASS   立即对事件作出响应,执行关键时间的任务。
高于正常         ABOVE_NORMAL_PRIORITY_CLASS   在正常优先级与高优先级之间运行(Windows2000)。
正常           NORMAL_PRIORITY_CLASS   没有特殊调度需求
低于正常         BELOW_NORMAL_PRIORITY_CLASS   在正常优先级与空闲优先级之间运行(Windows2000)。
空闲           IDLE_PRIORITY_CLASS   在系统空闲时运行。
设置方法:
BOOL   SetPriorityClass(   HANDLE   hProcess,   DWORD   dwPriority   );
DWORD   GetPriorityClass(   HANDLE   hProcess   );
使用命令外壳启动一个程序时,该程序的起始优先级是正常优先级。如果使用Start命令来启动该程序,可以使用一个开关来设定应用程序的起始优先级。例如:
c:\> START   /LOW   CALC.EXE
Start命令还能识别/BELOWNORMAL、/NORMAL、/ABOVENORMAL、/HIGH和/REALTIME等开关。

15.   线程的相对优先级有哪些?
相对优先级   标识符   描述
关键时间         THREAD_PRIORITY_TIME_CRITICAL   对于实时优先级类线程在优先级31上运行,对于其他优先级类,线程在优先级15上运行。
最高           THREAD_PRIORITY_HIGHEST   线程在高于正常优先级上两级上运行。
高于正常         THREAD_PRIORITY_ABOVE_NORMAL   线程在正常优先级上一级上运行。
正常           THREAD_PRIORITY_NORMAL   线程在进程的优先级类上正常运行。
低于正常         THREAD_PRIORITY_BELOW_NORMAL   线程在低于正常优先级下一级上运行。
最低           THREAD_PRIORITY_LOWEST   线程在低于正常优先级下两级上运行。
空闲           THREAD_PRIORITY_IDLE   对于实时优先级类线程在优先级16上运行对于其他优先级类线程在优先级1上运行。
设置方法:
BOOL   SetThreadPriority(   HANDLE   hThread,   DWORD   dwPriority   );
DWORD   GetThreadPriorityClass(   HANDLE   hThread   );

16.   如何避免系统动态提高线程的优先级等级?
  系统常常要提高线程的优先级等级,以便对窗口消息或读取磁盘等I/O事件作出响应。或者当系统发现一个线程在大约3至4s内一直渴望得到CPU时间,它就将这个渴望得到CPU时间的线程的优先级动态提高到15,并让该线程运行两倍于它的时间量。当到了两倍时间量的时候,该线程的优先级立即返回到它的基本优先级。下面的函数可以对系统的调度方式进行设置:
BOOL   SetProcessPriorityBoost(   HANDLE   hProcess,   BOOL   bDisableBoost   );
BOOL   GetProcessPriorityBoost(   HANDLE   hProcess,   PBOOL   pbDisableBoost   );
BOOL   SetThreadPriorityBoost(   HANDLE   hThread,   BOOL   bDisableBoost   );
BOOL   GetThreadPriorityBoost(   HANDLE   hThread,   PBOOL   pbDisableBoost   );
SetProcessPriorityBoost负责告诉系统激活或停用进行中的所有线程的优先级提高功能,而SetThreadPriorityBoost则激活或停用各个线程的优先级提高功能。Windows98没有提供这4个函数的有用的实现代码。



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/449978.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AirPods的自动连接配对原理

首次连接 打开装有 AirPods 的充电盒,并将它放在 iPhone 旁边。此时你的 iPhone 上将出现设置动画。轻点「连接」,然后轻点「完成」。 就这么简单,而且会自动设置,实现与已使用同一 Apple ID 登录 iCloud 的任一支持设备搭配使用…

Linux chmod命令

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 Linux/Unix 的文件调用权限分为三级 : 文件拥有者、群组、其他。利用 chmod 可以藉以控制文件如何被他人所调用。 使用权限 : 所有使用…

模块化

我那进了"模块化研究"小组.所以嘞.研究模块化以及如何让项目的模块化更加合理和高效是我们小组的主要目的.首先,在实行模块化之前,得先巩固模块化开发的理论基础,因为理论是实践的基础。只有这样,在过程中理论与实践相结合,才有可能达到最满意…

1566:基础练习 十六进制转八进制

题目地址&#xff1a;https://acmore.cc/problem/LOCAL/1566 1 #include <iostream>2 #include <string>3 4 using namespace std;5 6 string HexToBin(string s) //16进制转2进制7 {8 string str "";9 for (int i 0; i < s.size(); i) 10…

利用fastjson对json转map的操作

String str "{\"0\":\"zhangsan\",\"1\":\"lisi\",\"2\":\"wangwu\",\"3\":\"maliu\"}"; //第一种方式 Map maps (Map)JSON.parse(str); System.out.println("这个是用J…

推荐书籍

五百本编程书籍推荐【信息化类】 书号书名作者出版时间定价对应页码TP02041企业资源计划&#xff08;ERP&#xff09;教程罗鸿2006-1&#xffe5;28.00—TP02031ERP理论、方法与实践周玉清 等2005-12&#xffe5;39.00—TP01059ERP原理设计实施&#xff08;第3版&#xff09;罗…

Linux diffstat命令

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 Linux diffstat命令根据diff的比较结果&#xff0c;显示统计数字。 diffstat读取diff的输出结果&#xff0c;然后统计各文件的插入&…

java命令--jmap命令使用(查找内存泄漏对象)

转自&#xff1a;https://www.cnblogs.com/kongzhongqijing/articles/3621163.html jdk安装后会自带一些小工具&#xff0c;jmap命令(Java Memory Map)是其中之一。主要用于打印指定Java进程(或核心文件、远程调试服务器)的共享对象内存映射或堆内存细节。 jmap命令可以获得运行…

tr069相关协议说明

截图自easycwmp官网&#xff1a;http://easycwmp.org/转载于:https://www.cnblogs.com/kiss-passion/p/10362029.html

如何revert一个merged branch上所有的改动

开发过程中如果想删除之前merged的某个branch&#xff0c;并且在merge过该分支之后又进行了多次的提交&#xff0c;可以通过以下命令进行&#xff1a; git revert -n merge_commit_id -m 1 注&#xff1a;该方法适合merge过分支后&#xff0c;没有基于该branch内容做修改的情况…

Beta 冲刺(6/7)

队名 火箭少男100组长博客 林燊大哥作业博客 Beta 冲鸭鸭鸭&#xff01;成员冲刺阶段情况 林燊&#xff08;组长&#xff09; 过去两天完成了哪些任务 协调组内工作最终测试文稿编写展示GitHub当日代码/文档签入记录(组内共享)接下来的计划 协助开发组完成标签制作展示视频制作…

Linux find命令、Linux rmdir命令、Linux ls命令

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 Linux find命令用来在指定目录下查找文件。任何位于参数之前的字符串都将被视为欲查找的目录名。如果使用该命令时&#xff0c;不设置任…

Android Bluetooth BLE相关开发资源汇总

Android开启蓝牙开关 转载自Android&#xff1a;Bluetooth 的打开和关闭 检查系统蓝牙是否开启 BluetoothManager bluetoothManager (BluetoothManager) this. getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter mBluetoothAdapter bluetoothManager.getAdapt…

__invoke,try{}catch(){},microtime(),is_callable()

<?php /*1.对象本身不能直接当函数用&#xff0c;如果被当做函数用&#xff0c;会直接回调__invoke方法* 2.验证变量的内容能否作为函数调用* 3.try{}catch(Exception $e){}catch(){}finally{}* 4.microtime()函数返回当前时间戳和微妙数* */ class httpException extends …

H.264中的I_PCM模式

H.264中的I_PCM模式 I_PCM是一种帧内编码模式&#xff0c;在该模式下&#xff0c;编码器直接传输图像的像素值&#xff0c;而不经过预测和变换。在一些特殊的情况下&#xff0c;特别是图像内容不规则或者量化参数非常低时&#xff0c;该模式比常规的操作&#xff08;帧内预测…

RxPermissions 源码解析之举一反三

[toc] RxPermissions 源码解析 简介 RxPermissions 是基于 RxJava 开发的用于帮助 在Android 6.0 中处理运行时权限检测的框架。在 Android 6.0 中增加了对危险权限的动态申请&#xff0c;而不是像 Android 6.0 之前的默认全部获取的方式。 原始动态权限的获取 如果按照以往的获…

总结Selenium WebDriver中一些鼠标和键盘事件的使用

在使用 Selenium WebDriver 做自动化测试的时候&#xff0c;会经常模拟鼠标和键盘的一些行为。比如使用鼠标单击、双击、右击、拖拽等动作&#xff1b;或者键盘输入、快捷键使用、组合键使用等模拟键盘的操作。在 WebDeriver 中&#xff0c;有一个专门的类来负责实现这些测试场…

最快浮点数取绝对值

做视频算法10多年&#xff0c;经常要算绝对值&#xff0c;整数的绝对值有快速算法&#xff0c;但浮点数的绝对值没看到有快速算法&#xff0c;经常不段发现&#xff0c;得到如下浮点数的快速算法&#xff1a; 快6倍多&#xff0c; #include <Windows.h> #include <ios…

Linux ln命令、软链接和硬链接的区别

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 Linux ln命令是一个非常重要命令&#xff0c;它的功能是为某一个文件在另外一个位置建立一个同步的链接。 当我们需要在不同的目录&…

Android应用开发——文件目录

Android 存储位置及 API 一、内部存储 应用安装后都会在Android 根目录生成 /data/data/packagename&#xff0c;当前应用读取不需要读写权限 注意&#xff1a; 有些开发者可能看到过应用的根目录为 /data/user/0/packagename 的情况&#xff0c;这里解释一下&#xff0c;And…