Torch Threads
最近在读openface源码的时候,对里面的线程不怎么清楚。然后就到github上读了下说明。
- Markdown和扩展Markdown简洁的语法
- 代码块高亮
- 图片链接和图片上传
- LaTex数学公式
- UML序列图和流程图
- 离线写博客
- 导入导出Markdown文件
- 丰富的快捷键
简介
你可能会想为什么另外开发一个基于lua的线程包?其实就我所知,目前存在的线程包作用十分有限,仅仅是创建线程,线程完成任务,结束线程这么简单。每当我想要使用线程完成某项任务的时候,总是创建线程的开销就远大于了我的工作量。而且在线程之间传递数据也非常不方便。torch提供的threads包有以下优点。
- 线程只被创建一次,其实就是使用线程池。加粗
Ctrl + B
- 任务以回调函数的形式传入线程系统,传入的回调函数将会被线程池空闲的线程执行。斜体
Ctrl + I
- 如果提供的有结束回调函数,那么在任务完成(回调函数执行完毕)之后将在主函数中执行结束回调函数。引用
Ctrl + Q
- 任务执行的时候是完全序列化的,允许线程之间进行数据copy。插入链接
Ctrl + L
- 回调函数返回的值将会传入结束回调函数中。插入代码
Ctrl + K
- 在结束回调函数中,可以直接使用主线程中的变量。插入图片
Ctrl + G
- 线程间的同步操作非常简便。提升标题
Ctrl + H
代码块
local threads = require 'threads'
local nthread = 4
local njob = 10
local msg = "hello from a satellite thread"
local pool = threads.Threads(
nthread,
function(threadid)
print('starting a new thread/state number ' .. threadid)
gmsg = msg -- get it the msg upvalue and store it in thread state
end
)
local jobdone = 0
for i=1,njob do
pool:addjob(
function()
print(string.format('%s -- thread ID is %x', gmsg, __threadid))
return __threadid
end,
function(id)
print(string.format("task %d finished (ran on thread ID %x)", i, id))
jobdone = jobdone + 1
end
)
end
pool:synchronize()
print(string.format('%d jobs done', jobdone))
pool:terminate()
通过阅读上面的说明,我以为在回调函数中无法访问主线程中的变量i,但是通过实例发现在回调函数和结束回调函数中都可访问主线程中的变量i,此外确定了一点,回调函数和结束回调函数都是被线程池里面的线程执行的,任何一方都不是被主线程执行的。而且每个回调函数和结束回调函数合并在一起作为任务被某个线程进行处理,在从回调函数到结束回调函数转换的过程中,并没有进行线程切换。
上面的代码通过threads.Threads创建了一个线程池。在pool:addjob里面加入要执行的回调函数和结束回调函数。pool:synchronize()简简单单一句代码就维持了线程之间的同步。pool:terminate()进行线程资源释放,应该是在所有任务都结束的时候,就会自动释放资源。
运行结果
starting a new thread/state number 1
starting a new thread/state number 3
starting a new thread/state number 2
starting a new thread/state number 4
hello from a satellite thread – thread ID is 1
hello from a satellite thread – thread ID is 2
hello from a satellite thread – thread ID is 1
hello from a satellite thread – thread ID is 2
hello from a satellite thread – thread ID is 4
hello from a satellite thread – thread ID is 2
hello from a satellite thread – thread ID is 1
hello from a satellite thread – thread ID is 3
task 1 finished (ran on thread ID 1)
hello from a satellite thread – thread ID is 4
task 2 finished (ran on thread ID 2)
hello from a satellite thread – thread ID is 4
task 3 finished (ran on thread ID 1)
task 4 finished (ran on thread ID 2)
task 5 finished (ran on thread ID 4)
task 9 finished (ran on thread ID 4)
task 10 finished (ran on thread ID 4)
task 8 finished (ran on thread ID 3)
task 6 finished (ran on thread ID 2)
task 7 finished (ran on thread ID 1)
10 jobs done
Library
library 提供了不同级别的线程功能。
- 中级的功能:
- Threads线程池、Queue线程队列、serialize序列化和反序列化线程、safe()创建一个安全的线程。
- 低级的功能:
- Thread创建一个简单的线程。Mutex创建自旋锁。Condition创建一个条件变量。Atomic counter 原子锁。
Threads:
threads包里面被threads.Threads()创建的类作用非常大,后面还可以做更高级功能的封装。
这个类一般都这样来用:
local threads = require 'threads'
local t = threads.Threads(4) -- create a pool of 4 threads
但是在之前的threads包里面只有一个类,一般都这样创建。
local Threads = require 'threads'
local t = Threads(4) -- create a pool of 4 threads
一个线程池里面一般都会管理好几个队列,后面介绍的基本上都是线程池内线程和任务的管理,比如从任务队列里面获取任务,以及线程结束后如何从回调函数传入到结束回调函数最后再进入主线程。
require 'nn'
local threads = require 'threads'
local model = nn.Linear(5, 10)
threads.Threads(
2,
function(idx) -- This code will crash
require 'nn' -- because the upvalue 'model'
local myModel = model:clone() -- is of unknown type before deserialization
end
)
require 'nn'
local threads = require 'threads'
local model = nn.Linear(5, 10)
threads.Threads(
2,
function(idx) -- This code is OK.
require 'nn'
end, -- child threads know nn.Linear when deserializing f2
function(idx)
local myModel = model:clone() -- because f1 has already been executed
end
)
threads.Threads(N,[f1,f2,…])在创建线程池的时候,可传入多个参数,一般都会在f1放入定义,而在后面的函数中放入代码。