易语言多线程封装线程启动返回句柄_易语言取线程句柄-程序员宅基地

技术标签: 线程ID  大漠  易语言  多线程  线程句柄  511遇见  启动线程  

封装多线程模块-线程启动

• 1、CreateThread
• 2、线程_启动_句柄()
• 3、线程_启动_逻辑()
• 4、线程句柄
• 5、线程ID
• 6、易语言SHCreateThread

511遇见易语言多线程大漠多线程


SHCreateThread function | Microsoft Docs https://docs.microsoft.com/zh-cn/windows/desktop/api/shlwapi/nf-shlwapi-shcreatethread

.版本 2

.参数 线程地址, 子程序指针, , pfnThreadProc
.参数 数据, 整数型, , pData,Any---0

SHCreateThread

指向包含初始化数据的可选应用程序定义数据结构的指针。它被传递给pfnThreadProc和pfnCallback(可选)指向的函数。这个值可以是NULL。
.参数 标志, 整数型, , dwFlags------0
控制函数行为的标志。一个或多个CTF常数。
.参数 返回信息, 整数型, , pfnCallback—0
SHCreateThread将等待pfnCallback所指向的函数返回,然后再返回给它的调用者。pfnCallback指向的函数的返回值被忽略。
这个函数,这是shlwapi.dll 的一个导出函数,这个函数看字面也能理解是个创建多线程的函数,而且。。。他是基于对象操作的多线程函数,相对比较稳定

API- Create Thread

CreateThread是一种微软在Windows API中提供了建立新的线程的函数,该函数在主线程的基础上创建一个新线程。线程终止运行后,线程对象仍然在系统中,必须通过CloseHandle函数来关闭该线程对象。
当某个程序创建一个线程后,会产生一个线程的句柄,线程的句柄主要用来控制整个线程的运行,例如停止、挂起或设置线程的优先级等操
第一个参数表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。是指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,它被设为NULL。//线程安全属性
第二个参数表示线程栈空间大小。传入0表示使用默认大小(1MB)。// 堆栈大小
第三个参数表示新线程所执行的线程函数地址,多个线程可以使用同一个函数地址。// 线程函数
第四个参数是传给线程函数ThreadProc的参数。这样主线程和从属线程就可以共享数据。 //线程参数
第五个参数指定额外的标志来控制线程的创建,为0表示线程创建之后立即就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用ResumeThread()。// 线程创建属性
第六个参数将返回线程的ID号,传入NULL表示不需要返回该线程ID号。// 线程ID
函数返回值:
成功返回新线程的句柄,失败返回0。
易语言模块API视频教程线程启动CreateThread
CreateThread使用(六个参数介绍)

function CreateThread(
  lpThreadAttributes: Pointer;           {安全设置}
  dwStackSize: DWORD;                    {堆栈大小}
  lpStartAddress: TFNThreadStartRoutine; {入口函数}
  lpParameter: Pointer;                  {函数参数}
  dwCreationFlags: DWORD;                {启动选项}
  var lpThreadId: DWORD                  {输出线程 ID }
): THandle; stdcall;                     {返回线程句柄}

CreateThread function | Microsoft Docs https://docs.microsoft.com/zh-cn/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createthread

1、返回值:返回线程句柄

"句柄" 类似指针, 但通过指针可读写对象, 通过句柄只是使用对象;
有句柄的对象一般都是系统级别的对象(或叫内核对象); 之所以给我们的是句柄而不是指针, 目的只有一个: "安全";
貌似通过句柄能做很多事情, 但一般把句柄提交到某个函数(一般是系统函数)后, 我们也就到此为止很难了解更多了; 事实上是系统并不相信我们.不管是指针还是句柄, 都不过是内存中的一小块数据(一般用结构描述), 微软并没有公开句柄的结构细节, 猜一下它应该包括: 真实的指针地址、访问权限设置、引用计数等等.既然 CreateThread 可以返回一个句柄, 说明线程属于 "内核对象".
实际上不管线程属于哪个进程, 它们在系统的怀抱中是平等的; 在优先级(后面详谈)相同的情况下, 系统会在相同的时间间隔内来运行一下每个线程, 不过这个间隔很小很小, 以至于让我们误以为程序是在不间断地运行.这时你应该有一个疑问: 系统在去执行其他线程的时候, 是怎么记住前一个线程的数据状态的?
有这样一个结构 TContext, 它基本上是一个 CPU 寄存器的集合, 线程是数据就是通过这个结构切换的, 我们也可以通过 GetThreadContext 函数读取寄存器看看.
附上这个结构 TContext(或叫: CONTEXT、_CONTEXT) 的定义:
PContext = ^TContext;
_CONTEXT = record
ContextFlags: DWORD;
Dr0: DWORD;
Dr1: DWORD;
Dr2: DWORD;
Dr3: DWORD;
Dr6: DWORD;
Dr7: DWORD;
FloatSave: TFloatingSaveArea;
SegGs: DWORD;
SegFs: DWORD;
SegEs: DWORD;
SegDs: DWORD;
Edi: DWORD;
Esi: DWORD;
Ebx: DWORD;
Edx: DWORD;
Ecx: DWORD;
Eax: DWORD;
Ebp: DWORD;
Eip: DWORD;
SegCs: DWORD;
EFlags: DWORD;
Esp: DWORD;
SegSs: DWORD;
end;

2、参数6:输出线程ID

CreateThread 的最后一个参数是 "线程的 ID";
既然可以返回句柄, 为什么还要输出这个 ID? 现在我知道的是:
1、线程的 ID 是唯一的; 而句柄可能不只一个, 譬如可以用 GetCurrentThread 获取一个伪句柄、可以用 DuplicateHandle 复制一个句柄等等.
2、ID 比句柄更轻便.
在主线程中 GetCurrentThreadId、MainThreadID获取的都是主线程的 ID.
MainInstance: Indicates the instance handle for the main executable.
Use MainInstance to obtain the instance handle for the main executable of an application. This is useful in applications that use runtime libraries or packages, when you need the handle for the executable rather than for the library.

3、参数5:启动选项

CreateThread 的倒数第二个参数 dwCreationFlags(启动选项) 有两个可选值:
0: 线程建立后立即执行入口函数;
CREATE_SUSPENDED: 线程建立后会挂起等待.
ResumeThread 恢复线程的运行; SuspendThread 挂起线程.
这两个函数的参数都是线程句柄, 返回值是执行前的挂起计数.
什么是挂起计数?
SuspendThread 会给这个数 +1; ResumeThread 会给这个数 -1; 但这个数最小是 0.
当这个数 = 0 时, 线程会运行; > 0 时会挂起.
如果被 SuspendThread 多次, 同样需要 ResumeThread 多次才能恢复线程的运行.
ResumeThread 和 SuspendThread 分别对应 TThread 的 Resume 和 Suspend 方法, 很好理解.

4、参数4:函数参数

线程入口函数的参数是个无类型指针(Pointer), 用它可以指定任何数据; 本例是把鼠标点击窗体的坐标传递给线程的入口函数, 每次点击窗体都会创建一个线程.

5、参数3:入口函数指针

到了入口函数了, 学到这个地方, 我查了一个入口函数的标准定义, 这个函数的标准返回值应该是 DWORD, 不过这函数在 Delphi 的 System 单元定义的是: TThreadFunc = function(Parameter: Pointer): Integer; 我以后会尽量使用 DWORD 做入口函数的返回值.
这个返回值有什么用呢?
等线程退出后, 我们用 GetExitCodeThread 函数获取的退出码就是这个返回值!
如果线程没有退出, GetExitCodeThread 获取的退出码将是一个常量 STILL_ACTIVE (259); 这样我们就可以通过退出码来判断线程是否已退出.
还有一个问题: 前面也提到过, 线程函数不能是某个类的方法! 假如我们非要线程去执行类中的一个方法能否实现呢?
尽管可以用 Addr(类名.方法名) 或 MethodAddress('published 区的方法名') 获取类中方法的地址, 但都不能当做线程的入口函数, 原因可能是因为类中的方法的地址是在实例化为对象时动态分配的.
后来换了个思路, 其实很简单: 在线程函数中再调用方法不就得了, 估计 TThread 也应该是这样.
CreateThread 第三个参数是函数指针, 新线程建立后将立即执行该函数, 函数执行完毕, 系统将销毁此线程从而结束多线程的故事.

6、参数2:堆栈大小

栈是私有的但堆是公用的
CreateThread 的第二个参数是分配给线程的堆栈大小.
这首先这可以让我们知道: 每个线程都有自己独立的堆栈(也拥有自己的消息队列).
什么是堆栈? 其实堆是堆、栈是栈, 有时 "栈" 也被叫做 "堆栈".
它们都是进程中的内存区域, 主要是存取方式不同(栈:先进后出; 堆:先进先出);
"栈"(或叫堆栈)适合存取临时而轻便的变量, 主要用来储存局部变量; 譬如 for i := 0 to 99 do 中的 i 就只能存于栈中, 你把一个全局的变量用于 for 循环计数是不可以的.
现在我们知道了线程有自己的 "栈", 并且在建立线程时可以分配栈的大小.
前面所有的例子中, 这个值都是 0, 这表示使用系统默认的大小, 默认和主线程栈的大小一样, 如果不够用会自动增长;
那主线程的栈有多大? 这个值是可以设定的: Project -> Options -> Delphi Compiler -> Linking(如图)
栈是私有的但堆是公用的, 如果不同的线程都来使用一个全局变量有点乱套;
为解决这个问题 Delphi 为我们提供了一个类似 var 的 ThreadVar 关键字, 线程在使用 ThreadVar 声明的全局变量时会在各自的栈中留一个副本, 这样就解决了冲突. 不过还是尽量使用局部变量, 或者在继承 TThread 时使用类的成员变量, 因为 ThreadVar 的效率不好, 据说比局部变量能慢 10 倍.

7、参数1:安全设置

CreateThread 的第一个参数 lpThreadAttributes 是指向 TSecurityAttributes 结构的指针, 一般都是置为 nil, 这表示没有访问限制; 该结构的定义是:
//TSecurityAttributes(又名: SECURITY_ATTRIBUTES、_SECURITY_ATTRIBUTES)
_SECURITY_ATTRIBUTES = record
nLength: DWORD; {结构大小}
lpSecurityDescriptor: Pointer; {默认 nil; 这是另一个结构 TSecurityDescriptor 的指针}
bInheritHandle: BOOL; {默认 False, 表示不可继承}
end;
//TSecurityDescriptor(又名: SECURITY_DESCRIPTOR、_SECURITY_DESCRIPTOR)
_SECURITY_DESCRIPTOR = record
Revision: Byte;
Sbz1: Byte;
Control: SECURITY_DESCRIPTOR_CONTROL;
Owner: PSID;
Group: PSID;
Sacl: PACL;
Dacl: PACL;
end;
够复杂的, 但我们在多线程编程时不需要去设置它们, 大都是使用默认设置(也就是赋值为 nil).我觉得有必要在此刻了解的是: 建立系统内核对象时一般都有这个属性(TSecurityAttributes);
在接下来多线程的课题中要使用一些内核对象, 不如先盘点一下, 到时碰到这个属性时给个 nil 即可, 不必再费神.
{建立事件}
function CreateEvent(
lpEventAttributes: PSecurityAttributes; {!}
bManualReset: BOOL;
bInitialState: BOOL;
lpName: PWideChar
): THandle; stdcall;
{建立互斥}
function CreateMutex(
lpMutexAttributes: PSecurityAttributes; {!}
bInitialOwner: BOOL;
lpName: PWideChar
): THandle; stdcall;
{建立信号}
function CreateSemaphore(
lpSemaphoreAttributes: PSecurityAttributes; {!}
lInitialCount: Longint;
lMaximumCount: Longint;
lpName: PWideChar
): THandle; stdcall;
{建立等待计时器}
function CreateWaitableTimer(
lpTimerAttributes: PSecurityAttributes; {!}
bManualReset: BOOL;
lpTimerName: PWideChar
): THandle; stdcall;上面的四个系统内核对象(事件、互斥、信号、计时器)都是线程同步的手段, 从这也能看出处理线程同步的复杂性; 不过这还不是全部, Windows Vista 开始又增加了 Condition variables(条件变量)、Slim Reader-Writer Locks(读写锁)等同步手段.不过最简单、最轻便(速度最快)的同步手段还是 CriticalSection(临界区), 但它不属于系统内核对象, 当然也就没有句柄、没有 TSecurityAttributes 这个安全属性, 这也导致它不能跨进程使用; 不过写多线程时一般不用跨进程啊, 所以 CriticalSection 应该是最常用的同步手段.
原文链接:http://www.cnblogs.com/del/category/174761.html

源码:

.版本 2
.支持库 EThread

.子程序 _按钮1_被单击

编辑框4.内容 = 到文本 (启动线程 (&子程序1, , e_hwnd))
编辑框1.内容 = 到文本 (e_hwnd)

.子程序 子程序1
.局部变量 i

.判断循环首 (e_是否启动 = 假)
i = i + 1
列表框1.加入项目 (到文本 (i), )
标签1.标题 = 到文本 (列表框1.取项目数 ())
延时 (1)
处理事件 ()
.判断循环尾 ()

.子程序 _按钮2_被单击

a_hwnd = 线程_启动_句柄 (&子程序2, , a_thid)
编辑框2.内容 = 到文本 (a_hwnd)
编辑框3.内容 = 到文本 (a_thid)

.子程序 子程序2
.局部变量 n

.判断循环首 (a_是否启动 = 假)
n = n + 1
列表框2.加入项目 (到文本 (n), )
标签2.标题 = 到文本 (列表框2.取项目数 ())
延时 (1)
处理事件 ()
.判断循环尾 ()

.子程序 _按钮3_被单击

e_是否启动 = 真
线程_关闭句柄 (e_hwnd)

.子程序 _按钮4_被单击

a_是否启动 = 真
线程_关闭句柄 (a_hwnd)

模块源码:

.版本 2
.程序集 集_多线程

.子程序 线程_启动_句柄, 整数型, 公开, 成功返回线程句柄,失败返回0
.参数 要启动的子程序, 子程序指针, , 要启动的子程序
.参数 要传去子程序的参数, 整数型, 可空, 往子程序传递一个整数型参数
.参数 线程ID, 整数型, 参考 可空, 整数型的变量,用于存储新创建的线程ID

返回 (CreateThread (0, 0, 要启动的子程序, 要传去子程序的参数, 0, 线程ID))

.子程序 线程_关闭句柄, 逻辑型, 公开, 关闭一个线程,关闭后线程继续运行,但是无法对这个线程操作
.参数 线程句柄, 整数型

返回 (CloseHandle (线程句柄))

源码:511遇见易语言大漠多线程系列免费视频教程

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zcp528/article/details/106429226

智能推荐

软件测试——接口常见问题汇总_接口测试能发现哪些问题-程序员宅基地

文章浏览阅读4.8k次,点赞6次,收藏25次。今天我们来聊聊接口设计用例设计,说到这个接口,相信绝大多数的测试员都有遇到过某些棘手的问题,那么今天我们就来总结一下在接口方面会遇到的难题。接口测试用例可以从功能、性能、安全三方面进行入手,设计。详情参看以下思维导图:接口技术层面1、输入参数验证校验不全面。如:2、接口内部触发的服务相关逻辑问题。如:3、接口返回值内容不符合要求。4、接口功能安全性问题。5、接口性能问题。6、接口请求和wiki文档不一致。7、接口地址变更导致不可用问题。接口业务层面1、接口返回内容不符合业务需求。2、接口涉及的落地数据问题。_接口测试能发现哪些问题

Verilog刷题记录01_do not use logical operators: "&& in assignment st-程序员宅基地

文章浏览阅读3.4k次,点赞20次,收藏41次。Verilog自学记录,大二萌新,苦于数逻久矣。整理了HDLBits的相关解答。让我们一键(退学)学习。_do not use logical operators: "&& in assignment statements

JEDI Visual Component Library-程序员宅基地

文章浏览阅读418次。JEDI Visual Component LibraryJVCL is a library of over 600 Delphi visual and non-visual Delphi/C++Builder components. It supports Delphi/C++Builder 6 and newer.The library is built upon code d_jedi visual

04MySQL数据库字段详解_字段名称类型长度字段说明主键默认值-程序员宅基地

文章浏览阅读1.3k次。一、字段类型学习目标:了解字段类型的作用,掌握MySQL中的字段类型,熟练运用字段类型来设计数据表字段类型的作用整数类型小数类型时间日期类型字符串类型1、字段类型作用目标:了解为什么要有字段类型以及字段类型的作用概念字段类型:MySQL中用来规定实际存储的数据格式字段类型在定义表结构时设定设定好字段类型后,插入数据时必须与字段类型对应,否则数据错误MySQL有四大数据类型整数类型小数类型字符串类型时间日期类型示例规定类型的字段只能插入相应的_字段名称类型长度字段说明主键默认值

【SVM回归预测】基于鹈鹕算法优化卷积神经网络结合支持向量机实现POA-CNN-SVM实现数据回归预测附matlab代码-程序员宅基地

文章浏览阅读903次,点赞19次,收藏20次。本文提出了一种基于鹈鹕算法优化卷积神经网络结合支持向量机(POA-CNN-SVM)的数据回归预测方法。该方法首先利用鹈鹕算法优化卷积神经网络的参数,然后将优化后的卷积神经网络与支持向量机相结合,构建POA-CNN-SVM回归模型。最后,利用POA-CNN-SVM回归模型对数据进行回归预测。实验结果表明,该方法能够有效提高数据回归预测的准确性。

YOLO_V2的region_layer LOSS损失计算源码解读_yolov2–region层源码-程序员宅基地

文章浏览阅读3.1k次,点赞3次,收藏5次。region_layer.cbox get_region_box(float *x, float *biases, int n, int index, int i, int j, int w, int h, int stride){ box b; b.x = (i + x[index + 0*stride]) / w; b.y = (j + x[index + 1*st_yolov2–region层源码

随便推点

linux设备树语法-程序员宅基地

文章浏览阅读351次。设备树语法及绑定概述Device Tree是一种用来描述硬件的数据结构,类似板级描述语言,起源于OpenFirmware(OF)。就ARM平台来说,设备树文件存放在arch/arm/boot/dts下,绑定文档存在Documentation/devicetree/bindings下。设备树由一系列被命名的节点(node)和属性(property)组成,而节点本身可包含子节点。所谓属..._warning: dt compatible string

Python批量调整Word文档中的字体、段落间距及格式python调用函数批量调整word格式_python进行word文档编辑 times new roman-程序员宅基地

文章浏览阅读7.3k次,点赞12次,收藏74次。利用Python定义函数批量处理Word中的字体格式样式,间距行距、字体设置。_python进行word文档编辑 times new roman

单片机原理及应用以及单机片的主要分类-程序员宅基地

文章浏览阅读1.2k次。导读:现代单片机渗透到我们生活的各个领域,几乎很难找到哪个领域没有单片机的踪迹。导弹的导航装置,飞机上各种仪表的控制,计算机的网络通讯与数据传输,工业自动化过程的实时控制和数据处理,广泛使用的各种智能IC卡,轿车的安全保障系统,录像机、摄像机、全自动洗衣机的控制,以及程控玩具、等等,这些都离不开单片机。在单片机入门系列讲座中,首先学习单片机的基本构成和工作原理、以及外围功能电路,然后,挑战一个_单片机原理及应用

表单式工作流功能模块设计方案-程序员宅基地

文章浏览阅读9.7k次,点赞34次,收藏33次。最近一个项目中需要独自设计一个表单式工作流功能模块,在此将整个功能模块的设计思路分享出来。_功能模块设计

UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte 0xf9 in position 56: illegal multibyte sequence_gbk' codec can't decode byte 0xf9 in position 56: -程序员宅基地

文章浏览阅读864次。抽空慢慢把载入数据csv/txt文件时出现的UnicodeDecodeError报错问题的解决方案整理出来,虽然是玄学问题,但是多试试方法总还是可以解决的(特别是处理出来几十万量级的数据集出现这个问题,心态比较容易稳不住)方法一:将文件用txt文本格式打开,另存为时选择utf-8编码格式,然后转回csv格式方法二:直接用office或wps将报错的csv文件打开,然后另存一份替换原文件..._gbk' codec can't decode byte 0xf9 in position 56: illegal multibyte sequenc

万众瞩目,谷歌的反击来了!全新PaLM 2反超GPT-4,办公全家桶炸裂升级,Bard史诗进化...-程序员宅基地

文章浏览阅读423次。Datawhale干货最新:谷歌 PaLM 2,来源:量子位万众瞩目,谷歌的反击来了。现在,谷歌搜索终于要加入AI对话功能了,排队通道已经开放。当然这还只是第一步。大的还在后面:全新大语言模型PaLM 2正式亮相,谷歌声称它在部分任务超越GPT-4。Bard能力大更新,不用再排队等候,并支持新语言。谷歌版AI办公助手也一并推出,将在Gmail中抢先亮相。谷歌云也上线多个基础大模型,为行业提供更..._连通应用全家桶,谷歌推出bard拓展程序,追赶gpt

推荐文章

热门文章

相关标签