异步编程和多线程
异步编程
通常,任务在某个线程中顺序或同步执行。 如果线程正在运行
在单核计算机上,并且任务是阻塞的,这意味着程序必须完成
线程在继续执行其他任务之前。
异步任务,也称为协程,旨在在单个线程内最大限度地提高效率
通过将任务划分为多个线程并允许内核在之间快速切换
他们。 这意味着可以在不不必要地使用系统资源的情况下启动和停止任务。
换句话说,协程提供非阻塞执行。
当任务包括事件处理、生产者-消费者进程或
等待网络请求或文件操作等I/O操作。
例如,执行一些
数据转换需要很长时间。 此过程由以下内容描述
功能:
function long_process()
sleep(3) # имитация длительного процесса
return 42.0 # возврат некоторого результата
end
如果您需要处理大量数据,此过程将花费太长时间。 例如,执行
下面的代码块只需要9秒多一点,因为3个函数是按顺序调用的
,每个运行约3秒。:
@elapsed begin
p1 = long_process() # имитация длительного преобразования данных
p2 = long_process() # имитация длительного преобразования данных
p3 = long_process() # имитация длительного преобразования данных
end
上面的代码块代表了一个典型的场景,其中操作按顺序执行并导致累积执行时间。 幸运的是,总执行时间可以减少到最长的长度
过程。 这可以使用协程,任务来实现:
@elapsed begin
t1 = Task(long_process); schedule(t1)
t2 = Task(long_process); schedule(t2)
t3 = Task(long_process); schedule(t3)
(fetch(t1), fetch(t2), fetch(t3))
end
同样的结果是在短短3秒内实现的。 每个long_process都启动了
作为一个单独的工作单元,或任务。 这种分离允许处理器在任务之间切换
在执行过程中,一次完成多个任务。
要获得相同的结果,您可以使用宏**@任务**:
@elapsed begin
t1 = @task long_process(); schedule(t1)
t2 = @task long_process(); schedule(t2)
t3 = @task long_process(); schedule(t3)
(fetch(t1), fetch(t2), fetch(t3))
end
多线程
在前面的示例中,每个任务都是在同一线程中与其他任务并行执行的。 从严格意义上说,这种并行性不是并行性。 异步任务可能非常有用,但有时需要真正的并行性。 朱莉娅允许
您可以一次在多个线程中调度任务。
Threads宏。@spawn可用于复盖long_process函数。 对于每个过程
,创建一个任务,并安排其在任何线程中的执行,只要它变得可用。:
@elapsed begin
s1 = Threads.@spawn long_process()
s2 = Threads.@spawn long_process()
s3 = Threads.@spawn long_process()
(fetch(s1), fetch(s2), fetch(s3))
end
以确保线程。@spawn创建和调度任务,您可以检查返回类型:
Threads.@spawn sleep(1)
通过使用Threads宏可以简化在多个可用线程中完成任务。@线程。 要执行多线程for循环,只需将前缀**@threads添加到循环中。
生成的代码块的执行需要~3秒,因为每个
long_process作为新任务启动,并在可用线程中分布(如使用@spawn**的示例中)。
Threads.@threads for _ in 1:3
result = long_process()
end
宏**@threads**简化了可用线程中任务的调度,但是如果没有数据输出的特殊设计-Channel(通道),则无法获得任务的结果。 创建通道时,您可以指定其类型和大小(输入数量已公布)。 例如,Channel12创建
可同时包含12个字符串元素的通道。 如果省略类型,则通道
将允许输入Any类型。 在不指定数据类型的情况下创建通道:
ch = Channel(32)
已创建空通道。 现在让我们使用put函数向其写入数据!:
Threads.@threads for _ in 1:3
result = long_process()
put!(ch, result) # запись результата выполнения задачи с помощью put! в канал ch
end
ch
Long_process函数的3个结果被写入通道。 为了显示其中的第一个,您需要使用fetch函数。:
fetch(ch)
要提取下一个通道元素,请使用取! 函数。:
take!(ch)
此函数从通道中提取和删除元素。
结论:
此示例演示了使用异步任务和多线程以及使用通道的数据输出的基础知识。