當乙個協同正在執行時,不能在外部終止他。只能通過顯示的呼叫 yield 掛起他的執行。對於某些應用來說這個不存在問題,但有些應用對此是不能忍受的。不存在搶占式呼叫的程式是容易編寫的。不需要考慮同步帶來的 bugs,因為程式中的所有執行緒間的同步都是顯示的。你僅僅需要在協同**超出臨界區時呼叫 yield 即可。
對非搶占式多執行緒來說,不管什麼時候只要有乙個執行緒呼叫乙個阻塞操作(blocking operation),整個程式在阻塞操作完成之前都將停止。對大部分應用程式而言,只是無法忍受的,這使得很多程式設計師離協同而去。下面我們將看到這個問題可以被有趣的解決。
第一,載入 luasocket 庫
require "luasocket"
host =
"www.w3.org"
file =
"/tr/rec-html32.html"
第三,開啟乙個 tcp 連線到遠端主機的 80 埠(http 服務的標準埠)
c =assert
(socket.
connect
(host,80)
) 上面這句返回乙個連線物件,我們可以使用這個連線物件請求傳送檔案
) receive 函式返回他送接收到的資料加上乙個表示操作狀態的字串。當主機斷開連線時,我們退出迴圈。
第四,關閉連線
c:close
()
使用協同機制重寫上面的**,在乙個函式內:
function download (host, file)
local c =
assert
(socket.
connect
(host,80)
) local count =
0-- counts number of bytes read
)while
true
do count = count + string.
len(s)
if status ==
"closed" then break end
end
c:close()
print
(file, count)
end
function receive (connection)
return connection:
receive(2
^10) end
在同步接受資料的方式下,函式接收資料時不能被阻塞,而是在沒有資料可取時yield,**如下:
function receive (connection)
connection:
timeout(0
)--donot block
local s, status = connection:
receive(2
^10)if status ==
"timeout" then
coroutine.
yield
(connection)
end
return s, status
end
呼叫函式 timeout(0)使得對連線的任何操作都不會阻塞。當操作返回的狀態為timeout 時意味著操作未完成就返回了。在這種情況下,執行緒 yield。非 false 的數值作為yield 的引數告訴分配器執行緒仍在執行它的任務。(後面我們將看到分配器需要 timeout連線的情況),注意:即使在 timeout 模式下,連線依然返回他接受到直到 timeout 為止,因此 receive 會一直返回 s 給她的呼叫者。
threads =
-- list of all live threads
function get (host, file)
-- create coroutine
local co = coroutine.
create
(function (
)download
(host, file)
end)
-- insert it in the list
table.
insert
(threads, co)
end
**中 table 中為分配器儲存了所有活動的執行緒。分配器**是很簡單的,它是乙個迴圈,逐個呼叫每乙個執行緒。並且從執行緒列表中
移除已經完成任務的執行緒。當沒有執行緒可以執行時退出迴圈。
function dispatcher (
)while
true
do local n = table.
getn
(threads)
if n ==
0 then break end -- no more threads to run
for i=
1,n do
local status, res = coroutine.
resume
(threads[i])if
not res then -- thread finished its task?
table.
remove
(threads, i)
break
end
end
end
end
host =
"www.w3c.org"
get(host,
"/tr/html401/html40.txt"
)get
(host,
"/tr/2002/rec-xhtml1-20020801/xhtml1.pdf"
)get
(host,
"/tr/rec-html32.html"
)get
(host,
"/tr/2000/rec-dom-level-2-core-20001113/dom2-core.txt"
)dispatcher()
-- main loop
為了避免這種情況出現,我們可以使用 luasocket 庫中的 select 函式。當程式在一組 socket 中不斷的迴圈等待狀態改變時,它可以使程式被阻塞。我們只需要修改分配器,使用 select 函式修改後的**如下:
function dispatcher (
)while
true
do local n = table.
getn
(threads)
if n ==
0 then break end -- no more threads to run
local connections =
for i=
1,n do
local status, res = coroutine.
resume
(threads[i])if
not res then -- thread finished its task?
table.
remove
(threads, i)
break
else
-- timeout
table.
insert
(connections, res)
end
end
if table.
getn
(connections)
== n then
socket.
select
(connections)
end
end
end
在內層的迴圈分配器收集連線表中timeout地連線,注意:receive將連線傳遞給yield,因此 resume 返回他們。當所有的連線都 timeout 分配器呼叫 select 等待任一連線狀態的改變。最終的實現效率和上乙個協同實現的方式相當,另外,他不會發生忙等待,比起順序實現的方式消耗 cpu 的時間僅僅多一點點。 LUA教程非搶占式多執行緒 38
對非搶占式多執行緒來說,不管什麼時候只要有乙個執行緒呼叫乙個阻塞操作 blocking operation 整個程式在阻塞操作完成之前都將停止。對大部分應用程式而言,只是無法忍受的,這使得很多程式設計師離協同而去。下面我們將看到這個問題可以被有趣的解決。require luasocket host ...
執行緒的搶占式和非搶占式排程
在乙個程序裡,執行緒的排程有搶占式或者非搶占的模式。在搶占模式下,作業系統負責分配 時間給各個程序,一旦當前的程序使用完分配給自己的 時間,作業系統將決定下乙個占用 時間的是哪乙個執行緒。因此作業系統將定期的中斷當前正在執行的執行緒,將 分配給在等待佇列的下乙個執行緒。所以任何乙個執行緒都不能獨佔 ...
執行緒的排程有搶占式或者非搶占
在乙個程序裡,執行緒的排程有搶占式或者非搶占的模式。在搶占模式下,作業系統負責分配 時間給各個程序,一旦當前的程序使用完分配給自己的 時間,作業系統將決定下乙個占用 時間的是哪乙個執行緒。因此作業系統將定期的中斷當前正在執行的執行緒,將 分配給在等待佇列的下乙個執行緒。所以任何乙個執行緒都不能獨佔 ...