บทความก่อนหน้า
Coroutine
ปกติการทำงานของโปรแกรมจะเป็นการทำงานเรียงลำดับไปทีละคำสั่งแต่ coroutine ในภาษา Lua เป็นการจำลองการทำงานแบบ multithreading หรือการแบ่งงานออกเป็นส่วนย่อยที่เรียกว่า thread ให้แยกทำงานไปพร้อมๆ กันได้ แต่ coroutine จะใช้วิธีสลับการทำงานระหว่างแต่ละ thread ทำให้ดูเหมือนว่าจะทำงานไปพร้อมกันแทนโดยแต่ละ thread ไม่ต้องรอให้ thread ทำงานเสร็จก่อนแต่จะไม่จัดการสลับงานอัตโนมัติเราต้องเป็นคนจัดการสั่งให้แต่ละ thread หยุดหรือเริ่มงานเอง
ตัวอย่างการทำงานแบบปกติ
thread1 กับ thread2 ทำงานเรียงลำดับตามปกติ
function thread1()
print"thread1..."
for i = 1, 3 do
print(i)
end
end
function thread2()
print"thread2..."
for i = 5, 7 do
print(i)
end
end
-- โปรแกรมหลัก
for i = 1, 2 do
print("#"..i)
thread1()
thread2()
end
-- ผลลัพธ์
--[[
#1 -- รอบที่ 1
thread1... -- thread1 เริ่มทำงาน
1
2
3
thread2... -- thread2 เริ่มทำงาน
5
6
7
#2 -- รอบที่ 2
thread1...
1
2
3
thread2...
5
6
7
]]
ตัวอย่างการทำงานแบบ Coroutine
มีรูปแบบการรับข้อมูลดังนี้
--Ex.1
thread2 = coroutine.create(function()
for i = 1, 3 do
print"thread2..."
print(i)
coroutine.yield() -- หยุดการทำงานของ thread ชั่วคราว
end
end)
thread1 = function()
for i = 5, 7 do
print"thread1..."
print(i)
coroutine.resume(thread2) -- สลับไป thread1
end
end
-- โปรแกรมหลัก
thread1()
-- ผลลัพธ์
--[[
thread1...
5
thread2...
1
thread1...
6
thread2...
2
thread1...
7
thread2...
3
]]
--Ex.2
thread1 = coroutine.create(function()
for i = 1, 2 do
print("thread1..."..coroutine.status(thread1))
print(i)
coroutine.yield() -- หยุดการทำงานของ thread ชั่วคราว
print("รอบที่ "..(i+1))
end
print"thread1...end"
end)
thread2 = coroutine.wrap(function()
print"thread2..."
for i = 5, 7 do
print(i)
coroutine.yield() -- หยุดการทำงานของ thread ชั่วคราว
end
end)
-- โปรแกรมหลัก
print("status0:"..coroutine.status(thread1))
for i = 1, 3 do
coroutine.resume(thread1) -- ทำงาน thread1 ต่อจากที่หยุดไว้
print("status"..i..":"..coroutine.status(thread1))
thread2() -- ทำงาน thread2 ต่อจากที่หยุดไว้
end
-- ผลลัพธ์
--[[
status0:suspended -- status ของ thread1 ที่โปรแกรมหลักก่อนเข้าลูป
thread1...running -- thread1 รอบที่ 1
1
status1:suspended -- status ของ thread1 ที่โปรแกรมหลักรอบที่ 1
thread2... -- thread2 รอบที่ 1
5
รอบที่ 2 -- thread1 รอบที่ 2 ( i==1 )
thread1...running -- i==2
2
status2:suspended -- status ของ thread1 ที่โปรแกรมหลักรอบที่ 2
6 -- thread2 รอบที่ 2
รอบที่ 3 -- thread1 รอบที่ 3 ( i==2 )
thread1...running -- i==3
3
thread1...end
status3:dead -- status ของ thread1 ที่โปรแกรมหลักรอบที่ 3
7 -- thread2 รอบที่ 3
]]
การใช้คำสั่ง thread1 = coroutine.create(function()...end) จะได้ thread1 เป็นข้อมูลประเภท thread ซึ่งต้องใช้ coroutine.resume(thread1) เพื่อเรียกให้ thread1 ทำงานจนเจอคำสั่ง coroutine.yield() จึงจะหยุดการทำงานของ thread1 เหมือนจบการทำงานในฟังก์ชันปกติและกลับมาทำงานอื่นต่อจนเมื่อมีการเรียก coroutine.resume(thread1) อีกครั้งจึงจะกลับไปทำงานใน thread1 ต่อจากคำสั่ง coroutine.yield() ล่าสุดในฟังก์ชันนั้น และสามารถใช้คำสั่ง coroutine.status(thread1) เพื่อตรวจสอบสถานะของ thread ได้
ส่วนการใช้คำสั่ง thread2 = coroutine.wrap(function()...end) จะได้ thread2 เป็นข้อมูลประเภท function ที่สามารถหยุดการทำงานชั่วคราวเมื่อเจอคำสั่ง coroutine.yield() ได้เหมือนกับ thread แต่สามารถเรียกใช้งานได้ด้วย function call ( thread2() ) เหมือนกับฟังก์ชันธรรมดา
การรับส่งค่าใน thread
stat [, var1, var2, ...] = coroutine.resume(thread, ...) จะรับค่า ... ส่งไปให้ฟังก์ชันใน thread และเรียก thread ทำงานและหยุดเมื่อเจอ coroutine.yield([val1, val2, ...]) และส่งค่า true ไปที่ตัวแปร stat ( ถ้า status เป็น dead จะคืนค่า false กับข้อความแสดงข้อผิดพลาดแทน ) กับค่า val ไปที่ตัวแปร var
ถ้าเป็นฟังก์ชันที่ใช้ coroutine.wrap() เมื่อเรียกใช้ฟังก์ชันจะเหมือนกับใช้ coroutine.resume() เรียก thread แต่จะคืนแต่ค่า val ที่ yield ออกมาเท่านั้น
--Ex.1
co1 = coroutine.create(function(n)
print(string.format("send %d to co",n))
n = coroutine.yield(2) -- ส่งค่า 2 กลับไปที่โปรแกรมหลัก
-- และรับค่าจากโปรแกรมหลักเก็บที่ n
print(string.format("send %d to co",n))
n = coroutine.yield(4) -- ส่งค่า 4 กลับไปที่โปรแกรมหลัก
-- และรับค่าจากโปรแกรมหลักเก็บที่ n
print(string.format("send %d to co",n))
end)
-- โปรแกรมหลัก
_, m = coroutine.resume(co1, 1) -- เรียก co ทำงานโดยส่งค่า 1 เป็น argument
-- และคืนค่าที่ yield ออกมา
print(string.format("received %d from co",m))
_, m = coroutine.resume(co1, 3) -- เรียก co ทำงานโดยส่งค่า 3 เป็น argument
-- และคืนค่าที่ yield ออกมา
print(string.format("received %d from co",m))
-- ผลลัพธ์
--[[
send 1 to co
received 2 from co
send 3 to co
received 4 from co
]]
--Ex.2
co2 = coroutine.wrap(function(n)
print(string.format("send %d to co",n))
n = coroutine.yield(2) -- ส่งค่า 2 กลับไปที่โปรแกรมหลัก
-- และรับค่าจากโปรแกรมหลักเก็บที่ n
print(string.format("send %d to co",n))
n = coroutine.yield(4) -- ส่งค่า 4 กลับไปที่โปรแกรมหลัก
-- และรับค่าจากโปรแกรมหลักเก็บที่ n
print(string.format("send %d to co",n))
end)
-- โปรแกรมหลัก
m = co2(1) -- เรียก co ทำงานโดยส่งค่า 1 เป็น argument
-- และคืนค่าที่ yield ออกมา
print(string.format("received %d from co",m))
m = co2(3) -- เรียก co ทำงานโดยส่งค่า 3 เป็น argument
-- และคืนค่าที่ yield ออกมา
print(string.format("received %d from co",m))
-- ผลลัพธ์
--[[
send 1 to co
received 2 from co
send 3 to co
received 4 from co
]]
นอกจากนี้ใน thread เองยังสามารถเรียกใช้ฟังก์ชันปกติซึ่งสามารถหยุดการทำงานชั่วคราวด้วย coroutine.yield() และเริ่มทำงานในฟังก์ชันนั้นต่อเมื่อเรียก thread นั้นทำงานต่อ
function iter(t)
for i = 1, #t do
coroutine.yield(i,t[i])
end
end
co = coroutine.create(function(t)
iter(t)
end)
-- โปรแกรมหลัก
local a = {"a","b","c"}
local s, index, value = coroutine.resume(co,a)
print(1, s, value, coroutine.status(co), index)
s, index, value = coroutine.resume(co)
print(2, s, value, coroutine.status(co), index)
s, index, value = coroutine.resume(co)
print(3, s, value, coroutine.status(co), index)
s, index, value = coroutine.resume(co)
print(4, s, value, coroutine.status(co), index)
-- ผลลัพธ์
--[[
1 true a suspended 1
2 true b suspended 2
3 true c suspended 3
4 true nil dead nil
5 false nil dead cannot resume dead coroutine
]]
ตอนต่อไปมารู้จัก Iterator กันครับ
บทความถัดไป
ความคิดเห็น
แสดงความคิดเห็น