多线程

什么是多线程?

多线程是指在一个程序中同时运行多个线程(后台任务),每个线程可以独立执行不同的任务。线程是程序执行的最小单元,多线程允许程序并发(同时)执行多个任务,从而提高程序的效率和响应性。

多线程的作用?

  • 提高程序性能:通过并发执行多个任务,充分利用多核CPU的计算能力。

  • 提高响应性:在图形用户界面(GUI)应用程序中,多线程可以防止主线程被长时间运行的任务阻塞,保持界面的响应性。

  • 简化模型:多线程可以将复杂的任务分解为多个简单的任务,每个任务由一个线程执行,简化程序设计。

使用多线程

在轻语言中支持通过多种方式使用多线程。视频教程:安卓APP高级开发之异步与多线程的使用

1.异步执行语句

该方式是使用多线程较为灵活的一种方式、适合在代码的任何位置使用;使用方法为:通过异步执行首异步执行尾关键字包裹指定代码、被包裹的代码将会在子线程中运行,如果要更新窗口内容,使用关键字 到主线程 切换线程到主线程即可。

视窗示例:

事件 按钮1.被单击(来源对象 为 对象,事件对象 为 鼠标事件)
    ' 只在子线程中执行任务,但不更新内容到窗口组件
    异步执行首
        变量 源码 = 取网页源码("http://www.vcnstudio.com","UTF-8",5000)
        调试输出(源码)
    异步执行尾
    ' 在子线程中获取到数据后,将任务从子线程中切换(跳出子线程)到主线程
    ' 然后将获取的内容、赋值给窗口中的编辑框
    异步执行首
        变量 源码 = 取网页源码("http://www.vcnstudio.com","UTF-8",5000)
        到主线程
        编辑框1.内容 = 源码
    异步执行尾
    ' 注意:在子线程中不能修改窗口中任何组件的内容、外观和尺寸
结束 事件

2.内置函数:启动线程

轻语言在支持多线程的平台应用端开发包中内置了线程操作函数:启动线程 只需一句代码就能开启子线程,该函数的参数接收一个无返回值、无参数的函数。

视窗示例:

事件 按钮1.被单击(来源对象 为 对象,事件对象 为 鼠标事件)
    ' 函数 子程序1 将在子线程中被执行
    启动线程(&子程序1)

    ' 使用拉姆达表达式传入待执行的代码,下方{ }代码将在子线程中被执行
    启动线程(()->{
        调试输出("我是通过拉姆达表达式设置的代码,可执行耗时操作")
    })
结束 事件

函数 子程序1()
    调试输出("我是在子线程中执行的代码;可执行耗时操作")
结束 函数

3.线程组件

线程组件是轻语言在支持多线程平台应用端开发包中提供的一个集成了消息传递及子线程任务的高级线程工具,线程组件通常为一个不可视组件。

视窗示例1:

事件 按钮1.被单击(来源对象 为 对象,事件对象 为 鼠标事件)
    ' 声明并创建线程对象
    变量 局_线程1 = 创建 线程()
    ' 启动线程并设置待执行的子程序函数、及监听从子线程中传出消息到主线程的函数
    局_线程1.启动(&执行子线程任务,&收到子线程传来的消息,"传入给子线程的数据值")
结束 事件

函数 执行子线程任务(源对象 为 线程,数据值 为 对象)
    ' 这里的代码将会在子线程中被执行
    调试输出("启动时传入的数据值:" + 数据值)
    ' 待执行的代码...
    ' 任务执行结束后、发送给主线程,这里的源对象为启动时的线程对象
    源对象.发送消息(1,"任务执行完毕要发给主线程的数据值")
结束 函数

函数 收到子线程传来的消息(源对象 为 线程,消息ID 为 整数型,消息值 为 对象)
    ' 当在子线程中调用发送消息时,这里将收到消息ID、及发出的消息值
    调试输出(消息ID + ":" + 消息值)
结束 函数

视窗示例2:

' 若该变量为真、则下方子线程函数将无限循环执行,为假将会跳出循环
变量 执行任务 为 逻辑型

事件 按钮1.被单击(来源对象 为 对象,事件对象 为 鼠标事件)
    执行任务 = 真
    ' 声明并创建一个子线程、在该线程中每隔 2 秒获取一次热点信息
    变量 局_线程 = 创建 线程()
    局_线程.启动(&执行子线程任务,&收到子线程传出的消息)
结束 事件

函数 执行子线程任务(源对象 为 线程,数据值 为 对象)
    ' 子线程是后台执行、因此可以使用无限循环且不卡顿程序界面
    判断循环(执行任务)
        变量 热点数据 = 取网页源码("https://f.m.suning.com/api/ct.do","UTF-8",5000)
        源对象.发送消息(1,热点数据)
        延时(2000)
    结束循环
结束 函数

函数 收到子线程传出的消息(源对象 为 线程,消息ID 为 整数型,消息值 为 对象)
    如果(消息ID == 1)
        编辑框1.内容 = "新数据:" + 到文本(消息值)
    结束 如果
结束 函数

事件 按钮2.被单击(来源对象 为 对象,事件对象 为 鼠标事件)
    ' 跳出循环、停止执行任务
    执行任务 = 假
    编辑框1.内容 = "已停止获取数据"
结束 事件

线程同步

在同时开启多个子线程时,如果这些子线程中都用到了同一个变量或对象资源,由于子线程的执行是无序的,因此就会导致变量或资源出现抢占问题,例如当AB两个子线程读写同一个文件内容时,A 线程刚读到一半、B 线程就写入了新的数据,此时文件内容将会发生无法预估的错误。

为此要想解决这种问题、就需要给文件加锁,加锁的目的是让同一时刻、这个文件只能由一个线程读写,这样就能避免文件被抢占的问题,这种解决方案也叫线程同步。

示例:

' 声明并创建一个资源对象
变量 资源对象 = 创建 对象()

事件 按钮1.被单击(来源对象 为 对象,事件对象 为 鼠标事件)
    启动线程(()->{
        ' 在需要加锁的位置、对资源加锁即可
        加锁(资源对象){
            ' 执行操作...

        }
    })
结束 事件

加锁位置可以在任意子线程中执行的代码里加锁,若部分平台未升级最新版、可以使用:synchronizedlock 关键字代替中文关键字。