B4X编程语言:B4X的可恢复子程序(Sleep及Wait For的使用)

news2025/1/4 5:58:45

        可恢复子程序 (Resumable Subs) 是 B4A v7.00 / B4i v4.00 / B4J v5.50 中添加的一项新功能。它大大简化了异步任务的处理。可恢复子程序的特殊功能是它们可以暂停,稍后再恢复,而无需暂停执行主线程。程序不会等待可恢复子程序继续。其他事件将照常引发。
        任何具有一个或多个 Sleep 或 Wait For 调用的子程序都是可恢复子程序。

        一、Sleep

        1、语法及释义
        语法:Sleep (Milliseconds As Int)
        暂停当前例程执行并在指定时间Milliseconds毫秒后恢复。
        Milliseconds:时间延迟的毫秒数。
        例如:Sleep (1000) ' 当前例程将暂停 1000 毫秒,然后恢复。

        2、刷新 UI
        您可以调用 Sleep(0) 来实现最短的暂停。这可用于刷新 UI。它是 DoEvents 的一个很好的替代方案(B4J 和 B4i 中不存在 DoEvents,在 B4A 中应避免使用DoEvents)。
        示例:
        在程序主界面添加两个按钮Button1、Button2和一个文本标签Label1,添加两个按钮点击事件如下:

Private Sub Button1_Click	
	For i = 1 To 5000000
		Label1.Text=i		
	Next
	Log("Button1_Click finished!")
End Sub

Private Sub Button2_Click
	For i = 1 To 5000000
		Label1.Text=i
		If i Mod 1000 = 0 Then Sleep(0) '允许 UI 每 1000 次迭代刷新一次。
	Next
	Log("Button2_Click finished!")
End Sub

        当点击Button1后,Label1只在循环完成后才显示最终结果5000000。
        当点击Button2后,Label1在循环期间每隔1000次刷新显示一次i的数值。

        二、Wait For

        B4X 编程语言是事件驱动的。异步任务在后台运行,并在任务完成时引发事件。使用 Wait For 关键字,您可以在当前子程序中处理事件。

        1、等待地图加载示例
        例如,此代码将等待 GoogleMap Ready 事件(B4J 示例):

Sub AppStart (Form1 As Form, Args() As String)
	MainForm = Form1
	MainForm.RootPane.LoadLayout("1") 	'Load the layout file.
	
	gmap.Initialize("gmap")
	Pane1.AddNode(gmap.AsPane, 0, 0, Pane1.Width, Pane1.Height)
	MainForm.Show
	Wait For gmap_Ready	 '<----------------
	gmap.AddMarker(10, 10, "Marker")
End Sub

        2、等待文件下载示例
        使用 FTP 的稍微复杂一点的示例:列出远程文件夹中的所有文件,然后下载所有文件:

Sub DownloadFolder (ServerFolder As String)
	FTP.List(ServerFolder)
	Wait For FTP_ListCompleted (ServerPath As String, Success As Boolean, Folders() As  FTPEntry, Files() As FTPEntry) '<----
	If Success Then
		For Each f As FTPEntry In Files
			FTP.DownloadFile(ServerPath & f.Name, False, File.DirApp, f.Name)					 
            Wait For FTP_DownloadCompleted (ServerPath2 As String, Success As Boolean)
			Log($"File ${ServerPath2} downloaded. Success = ${Success}"$)
		Next
	End If
	Log("Finish")
End Sub

        当调用 Wait For 关键字时,子程序会暂停,内部事件调度程序会在等待的事件完成后将其恢复。如果等待的事件一直未完成,则子程序将永远不会恢复。但程序仍将完全响应其它事件。
        如果稍后使用同一事件调用 Wait For,则新的子程序实例将取代前一个。

        3、等待下载图像示例
        假设我们要创建一个下载图像并将其设置为 ImageView 的子程序:
        '第一个示例:不正确的示例。

Sub DownloadImage(Link As String, iv As ImageView)
	Dim job As HttpJob
	job.Initialize("", Me) '请注意,已不再需要名称参数。
	job.Download(Link)
	Wait For JobDone(job As HttpJob)
	If job.Success Then
		iv.SetImage (job.GetBitmap) '在B4A/B4i中用iv.Bitmap=job.GetBitmap替换
	End If
	job.Release
End Sub

        如果我们只调用它一次,它会正常工作(更准确地说,如果我们在前一次调用完成之前不再调用它)。如果我们像这样调用它:
        DownloadImage("https://www.b4x.com/images3/android.png", ImageView1)
        DownloadImage("https://www.b4x.com/images3/apple.png", ImageView2)
        这样的话只会显示第二幅图像,因为第二次调用 Wait For JobDone 将覆盖前一个图像。为了解决这个问题,Wait For 可以根据事件发送者区分事件。
        这是通过一个可选参数完成的:Wait For (<sender>) <event signature>
        '第二个示例:正确的示例。

Sub DownloadImage(Link As String, iv As ImageView)
   Dim job As HttpJob
   job.Initialize("", Me) '请注意,已不再需要名称参数。
   job.Download(Link)
   Wait For (job) JobDone(job As HttpJob)
   If job.Success Then
     iv.SetImage (job.GetBitmap) '在B4A/B4i中用iv.Bitmap=job.GetBitmap替换
   End If
   job.Release
End Sub

        通过上述代码,每个可恢复的子实例将等待不同的事件,并且不会受到其他调用的影响。
        不同之处在于 Wait For 行:
        错误的: Wait For JobDone(job As HttpJob)
        正确的: Wait For (job) JobDone(job As HttpJob)

        三、使用Sleep/Wait For的程序的代码流

        1、先看下面两个子程序,体会一下使用Sleep时代码的执行顺序:

Sub S1
	Log("S1: A")
	S2
	Log("S1: B")
End Sub

Sub S2
	Log("S2: A")
	Sleep(0)
	Log("S2: B")
End Sub

        我们执行S1时将输出下面结果:
        S1: A
        S2: A
        S1: B
        S2: B
        说明每当调用 Sleep 时,当前子程序都会暂停。这相当于调用 Return。

        2、再看下面两个子程序,体会一下使用Sleep和Wait For时代码的执行顺序:
        当一个子例程调用第二个可恢复的子例程时,第一个子例程中的代码将在第一个Sleep 或 Wait For调用之后继续向下执行。如果您想等待第二个子例程完成,那么您可以从第二个子例程中引发一个事件,并在第一个子例程中等待它:

Sub FirstSub 
	Log("FirstSub started")
	SecondSub
	Wait For SecondSub_Complete
	Log("FirstSub completed")
End Sub

Sub SecondSub 
	Log("SecondSub started")
	Sleep(1000)
	Log("SecondSub completed")
	CallSubDelayed(Me, "SecondSub_Complete")
End Sub

        我们执行FirstSub时日志输出结果:
        FirstSub started
        SecondSub started
        SecondSub completed
        FirstSub completed

        注意:
        - 使用 CallSubDelayed 比使用 CallSub 更安全。如果第二个子程序从未暂停(例如,如果仅根据某些条件调用 Sleep),CallSub 将失败。
        - 这里有一个假设,即 FirstSub 在完成之前不会再次被调用。

        四、可恢复子程序返回值

        可恢复子程序可以返回一个 ResumableSub 值。
        示例:

Sub Button1_Click
	Sum(1, 2)
	Log("after sum")
End Sub

Sub Sum(a As Int, b As Int)
	Sleep(100) '这将导致代码流返回到父级
	Log(a + b)
End Sub

        输出:
        after sum
        3
        这就是为什么不能简单地返回一个值的原因。

        解决方案:
        
可恢复的子程序可以返回一个名为 ResumableSub 的新类型。其他子程序可以使用此值等待子程序完成并获取所需的返回值。

Sub Button1_Click
	Wait For(Sum(1, 2)) Complete (Result As Int)
	Log("result: " & Result)
	Log("after sum")
End Sub

Sub Sum(a As Int, b As Int) As ResumableSub
	Sleep(100)
	Log(a + b)
	Return a + b
End Sub

        输出:
        3
        result: 3
        after sum

        上述 Button1_Click 代码等效于:

Sub Button1_Click
   Dim rs As ResumableSub = Sum(1, 2)
   Wait For(rs) Complete (Result As Int)
   Log("result: " & Result)
   Log("after sum")
End Sub

        使用可恢复子程序返回值所需步骤如下:
        1、将 As ResumableSub 添加到可恢复子程序签名中。
        2、使用您想要返回的值调用 Return。
       3、在调用子程序中,使用 Wait For (<sub here>) Complete (Result As <matching type>) 调用可恢复子程序。

        注意事项和提示:
        如果您不需要返回值但仍想等待可恢复子程序完成,则从可恢复子程序返回 Null,并将调用子程序中的类型设置为 Object。
        多个子程序可以安全地调用可恢复子程序。完成事件将到达正确的父级。
        您可以在其他模块中等待可恢复子程序(在 B4A 中,它仅与类相关)。
        可以更改结果参数名称。

        五、 KeyPress 和 Wait For MsgBox2Async(仅B4A)

        在B4A中,经常检查Back键,以防止用户无意中退出程序。您可以使用以下代码:

Sub Activity_KeyPress (KeyCode As Int) As Boolean '返回True以销毁该事件 
	Select KeyCode
		Case KeyCodes.KEYCODE_BACK
			OpenMsgBox
			Return True
		Case Else
			Return False
	End Select
End Sub

Sub OpenMsgBox
	Private Answ As Int
	Msgbox2Async("Do you want to exit?", "Exit", "Yes", "", "No", Null, False) 
	Wait For Msgbox_Result (Answ As Int)
	If Answ = DialogResponse .POSITIVE Then
		Activity.Finish
	End If
End Sub

        六、对话框

        模态对话框 = 保持主线程直到对话框关闭的对话框。
        如上所述, 模态对话框与DoEvents实现方式相同。 因此,建议切换到新的异步对话框。使用Wait For是一个简单不错的选择:
        你应该使用如下代码:

Msgbox2Async("Delete?", "Title", "Yes", "Cancel", "No", Null, False)
Wait For Msgbox_Result (Result As Int)
If Result = DialogResponse .POSITIVE Then
	' ...
End If

        而不是下面的代码:

Dim res As Int = Msgbox2("Delete?", "Title", "Yes", "Cancel", "No", Null) 
If res = DialogResponse.POSITIVE Then
	'...
End If

        Wait For不会占用主线程,相反,它保存当前的子例程状态并释放它。 当用户单击其中一个对话框按钮时,代码将会恢复。其他类似的新方法有:MsgboxAsync、InputListAsync 和 InputMapAsync。
        除了MsgboxAsync外,新方法还添加了一个新的可取消参数。 如果条件为真,则可以通过单击后退键或对话框外部取关闭话框。 这也是旧方法的默认行为。
        由于其他代码可以在异步对话框可见时运行,因此有可能会同时出现多个对话框。如果这种情况与你的应用程序相关,那么你应该在Wait For中设置发送者过滤器参数:

Dim sf As Object = Msgbox2Async("Delete?", "Title", "Yes", "Cancel", "No", Null, False) 
Wait For (sf) Msgbox_Result (Result As Int)
If Result = DialogResponse .POSITIVE Then
	'...
End If

        这允许显示多个消息,并会正确处理结果事件。

        六、SQL 使用 Wait For

        可恢复子例程的新特性,使处理大型数据集变得更简单,且对程序响应性的影响最小。
        插入数据的新标准方法是:

For i = 1 To 1000
	SQL1.AddNonQueryToBatch("INSERT INTO table1 VALUES (?)", Array(Rnd(0, 100000))) 
Next
Dim SenderFilter As Object = SQL1.ExecNonQueryBatch("SQL")
Wait For (SenderFilter) SQL_NonQueryComplete (Success As Boolean)
Log("NonQuery: " & Success)

        插入数据的新标准方法步骤步骤如下:
        - 对每个应该发出的命令调用 AddNonQueryToBatch 。
        - 使用 ExecNonQueryBatch执行命令,这是一种异步方法。这个命令将在后台执行,完成后将引发NonQueryComplete事件。
        - 此调用返回一个可用作发送方过滤器参数的对象。 这一点很重要,因为可能会运行多个后台批处理程序。 使用过滤器参数,在所有情况下,正确的 Wait For 调用都会捕获事件。
        - 注意:SQL1.ExecNonQueryBatch 在内部开始和结束事务。

        对于SQL查询:
        在大多数情况下,查询是很快的,因此一般使用同步查询SQL1.ExecQuery2。但是,如果有一个缓慢的查询,那么您应该使用SQL1.ExecQueryAsync:

Dim SenderFilter As Object = SQL1.ExecQueryAsync("SQL", "SELECT * FROM table1", Null) 
Wait For (SenderFilter) SQL_QueryComplete (Success As Boolean, rs As ResultSet)
If Success Then
	Do While rs.NextRow
		Log(rs.GetInt2(0))
	Loop
	rs.Close
Else
	Log(LastException)
End If

        与前面的情况一样,ExecQueryAsync方法返回一个用作发送者过滤器参数的对象。

        提示:
        1、B4A中的结果集类型扩展了cursor类型。如果您愿意,您可以将其更改为cursor。使用结果集的优点是它与B4J和B4i兼容。
        2、如果从查询返回的行数很多,那么在调试模式执行循环将会很慢。你可以通过把它放在一个不同的子例程中和清理项目(Ctrl + P)来加快查询速度:

Wait For (SenderFilter) SQL_QueryComplete (Success As Boolean, rs As ResultSet) 
	If Success Then
		WorkWithResultSet(rs)
	Else
		Log(LastException)
	End If
End Sub

Private Sub WorkWithResultSet(rs As ResultSet)
	Do While rs.NextRow
		Log(rs.GetInt2(0))
	Loop
	rs.Close
End Sub

        这与当前在可恢复子例程中禁用的调试器优化有关。在发布模式下,两种解决方案的性能将相同。

        注意事项和提示:
        
在大多数情况下,可恢复子例程在发布模式下的性能开销应该是很小的。 在调试模式下,开销可能会大些。 (如果这成为一个问题,那么可以将执行慢的代码放到能从可恢复子例程调用的其它子例程中)。
        • Wait For 事件处理程序先于常规的事件处理程序。
        • 可恢复子例程不会创建其他线程。 该代码由服务器解决方案中的主线程或处理程序线程执行。
        • 在B4J中的SQL使用Wait For,需要jSQL v1.50+,且建议将日志模式设置为WAL。

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

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

相关文章

智能化人才招聘系统是怎样的?

随着企业规模的扩大和业务范围的拓展&#xff0c;人才招聘成为了企业发展的关键环节。然而&#xff0c;市面上的人才招聘系统琳琅满目&#xff0c;质量参差不齐&#xff0c;许多企业发现&#xff0c;并非所有系统都能满足他们的需求&#xff0c;特别是智能化的需求。今天&#…

分布式专题(9)之Mysql高可用方案

一、分库分表概念 数据库&#xff0c;应该是一个应用当中最为核心的价值所在&#xff0c;也是开发过程中必须熟练掌握的工具。之前我们就学习过很多对MySQL的调优。但是随着现在互联网应用越来越大&#xff0c;数据库会频繁的成为整个应用的性能瓶颈。我们经常使用的MySQL数据库…

LockSupport的源码实现原理(一)

目录 底层源码分析 线程状态变化 许可证机制 中断处理 底层源码分析 public class LockSupport {// Unsafe实例private static final Unsafe U Unsafe.getUnsafe();// Thread对象中parkBlocker字段的偏移量private static final long PARKBLOCKER U.objectFieldOffset(Thre…

CannotRetrieveUpdates alert in disconnected OCP 4 cluster解决

环境&#xff1a; Red Hat OpenShift Container Platform (RHOCP) 4 问题&#xff1a; Cluster Version Operator 不断发送警报&#xff0c;表示在受限网络/断开连接的 OCP 4 集群中无法接收更新。 在隔离的 OpenShift 4 集群中看到 CannotRetrieveUpdates 警报&#xff1a; …

智能家居体验大变革 博联 AI 方案让智能不再繁琐

1. 全球AI技术发展背景及智能家居市场趋势 人工智能&#xff08;AI&#xff09;技术的飞速发展正在推动全球各行业的数字化转型。国际电信联盟与德勤联合发布《人工智能向善影响》报告指出&#xff0c;全球94%的商界领袖认为&#xff0c;人工智能技术对于其企业在未来5年内的发…

logback日志框架源码分析

目录 (一)入口:slf4j选择日志框架 (二)日志框架初始化 (1)logback的3种配置方式 a、BasicConfigurator默认配置 b、SPI方式配置的Configurator实现类 c、通过配置文件初始化 (2)xml配置文件初始化 (三)Logger的创建 (四)打印日志 本文源码基于:logback版…

VScode 只能运行c,运行不了c++的解决问题

原文链接&#xff1a;Vscode只能运行c&#xff0c;运行不了c的解决方法 VScode 只能运行c&#xff0c;运行不了c&#xff0c;怎么回事呢&#xff0c;解决问题&#xff1a; 在tasks.json中加上“"-lstdc"”&#xff0c; 这样之后 要重启VScode&#xff0c;点击链接…

HTML——45.单元格合并

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>表格</title></head><body><!--合并单元格&#xff1a;1.在代码中找到要合并的单元格2.在要合并的所有单元格中&#xff0c;保留要合并的第一个单元格…

记录第一次跑YOLOV8做目标检测

今天是24年的最后一天&#xff0c;终于要向新世界开始破门了&#xff0c;开始深度学习&#xff0c;YOLO来敲门~ 最近做了一些皮肤检测的功能&#xff0c;在传统的处理中经历了反复挣扎&#xff0c;终于要上YOLO了。听过、看过&#xff0c;不如上手体会过~ 1、YOLO是什么&#x…

如何配置Java应用程序的远程调试

配置Java应用程序的远程调试涉及几个步骤&#xff0c;包括在启动Java应用程序时设置特定的JVM参数&#xff0c;以及在IDE&#xff08;如Eclipse、IntelliJ IDEA等&#xff09;中配置远程调试连接。以下是详细的步骤&#xff1a; 启动Java应用程序时启用远程调试 为了能够远程…

2025考研江南大学复试科目控制综合(初试807自动控制原理)

​ 2025年全国硕士研究生招生考试江南大学考点 一年年的考研如期而至&#xff0c;我也变成了研二了&#xff0c;作为2次考研经历的学长&#xff0c;总是情不自禁地回想起自己的考研经历&#xff0c;我也会经常从那段经历中汲取力量。我能理解大多数考生考完后的的迷茫无助&…

时间序列预测算法---LSTM

文章目录 一、前言1.1、深度学习时间序列一般是几维数据&#xff1f;每个维度的名字是什么&#xff1f;通常代表什么含义&#xff1f;1.2、为什么机器学习/深度学习算法无法处理时间序列数据?1.3、RNN(循环神经网络)处理时间序列数据的思路&#xff1f;1.4、RNN存在哪些问题?…

[羊城杯 2024]不一样的数据库_2

题目描述&#xff1a; 压缩包6 (1).zip需要解压密码&#xff1a; 尝试用ARCHPR工具爆破一下&#xff1a; &#xff08;字典可自行在github上查找&#xff09; 解压密码为&#xff1a;753951 解压得到13.png和Kee.kdbx文件&#xff1a; 二维码图片看上去只缺了正常的三个角&…

图像处理-Ch7-小波函数

个人博客&#xff01;无广告观看&#xff0c;因为这节内容太多了&#xff0c;有点放不下&#xff0c;分了三节 文章目录 多分辨率展开(Multi-resolution Expansions)序列展开(Series Expansions)尺度函数(Scaling Function)例&#xff1a;哈尔尺度函数(Haar scaling func)多分…

CPT203 Software Engineering 软件工程 Pt.2 敏捷方法和需求工程(中英双语)

文章目录 3. Aglie methods&#xff08;敏捷方法&#xff09;3.1 Aglie methods&#xff08;敏捷方法&#xff09;3.1.1 特点3.1.2 优点3.1.3 缺点3.1.4 原则3.1.5 计划驱动与敏捷方法的对比 3.2 Scrum3.2.1 Scrum roles3.2.2 Scrum Activities and Artifacts3.2.2.1 Product B…

【人工智能机器学习基础篇】——深入详解深度学习之神经网络基础:理解前馈神经网络与反向传播算法

深入详解深度学习之神经网络基础&#xff1a;理解前馈神经网络与反向传播算法 深度学习作为人工智能&#xff08;AI&#xff09;的核心技术&#xff0c;已经在语音识别、图像处理、自然语言处理等诸多领域取得了显著的成果。而在深度学习的众多模型中&#xff0c;**前馈神经网络…

【循环神经网络】RNN介绍

在人工神经网络中&#xff0c;”浅层网络”是指具有一个输入层、一个输出层和最多一个没有循环连接的隐藏层的网络。随着层数的增加&#xff0c;网络的复杂性也在增加。更多的层或循环连接通常会增加网络的深度&#xff0c;并使其能够提供不同级别的数据表示和特征提取&#xf…

【论文投稿】Python 网络爬虫:探秘网页数据抓取的奇妙世界

【IEEE出版|广东工业大学主办】第五届神经网络、信息与通信工程国际学术会议&#xff08;NNICE 2025&#xff09;_艾思科蓝_学术一站式服务平台 目录 前言 一、Python—— 网络爬虫的绝佳拍档 二、网络爬虫基础&#xff1a;揭开神秘面纱 &#xff08;一&#xff09;工作原…

基于深度学习的视觉检测小项目(二) 环境和框架搭建

一、环境和框架要求 SAM的环境要求&#xff1a; Python>3.7 PyTorch>1.7 torchvision>0.8 YOLO V8的环境要求&#xff1a;YOLO集成在ultralytics库中&#xff0c;ultralytics库的环境要求&#xff1a; Python>3.7 PyTorch>1.10.0 1、确定pytorch版本…

SQLiteDataBase数据库

XML界面设计 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"android:layout_width"match_paren…