【Lua】初探协程

类似于线程,协程拥有自己独立的栈、局部变量和指令指针。多个协程共享全局变量和其它大部分东西。线程与协程的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协程却需要彼此协作的运行。就是说,一个具有多个协程的程序在任意时刻只能运行一个协程,并且正在运行的协程只会在其显式地要求挂起时,它的执行才会暂停。


Lua将所有关于协程的函数放置在一个名为“coroutine”的table中。函数create用于创建新的协程,它只有一个参数,就是一个函数。该函数的代码就是协程需要执行的内容。create会返回一个thread类型的值,用以表示新的协程,create的参数常用匿名函数,如以下代码:

local co = coroutine.create(function () print("Hello World") end)

Lua协程常用的函数有:

方法 描述
coroutine.create() 创建coroutine,返回coroutine, 参数是一个函数,当和resume配合使用的时候就唤醒函数调用
coroutine.resume() 启动coroutine,和create配合使用
coroutine.yield() 挂起正在调用的协程的执行。 传递给 yield 的参数都会转为 resume 的额外返回值。
coroutine.status() 查看coroutine的状态              注:coroutine的状态有四种:dead,suspend,running, normal
coroutine.wrap() 创建coroutine,返回一个函数,一旦你调用这个函数,就进入coroutine
coroutine.running() 返回正在跑的coroutine,一个coroutine就是一个线程,当使用running的时候,就是返回一个coroutine的线程号。此外,还返回一个布尔值,若当前协程为主线程,则为真,反之就为假

注意 wrap() 与 create() 函数的区别:wrap()返回的是函数,create()返回的是thread变量。所以wrap()的返回值是不能作为 status() 的参数的,status()函数的入参得是一个 thread变量。


下面通过例子说明各个函数用法:

co1 = coroutine.create(
	function(i)
		print(i)
		print("co1 status: ", coroutine.status(co1))  
       	print(coroutine.running()) 
	end
)
print("co1 status: ", coroutine.status(co1))  
coroutine.resume(co1, 1)
print("co1 status: ", coroutine.status(co1))  

co2 = coroutine.wrap(
	function(i)
		print(i)
        print(coroutine.running()) 
	end
)
co2(2)

co3 = coroutine.create(
    function(j)
		print(j)
        for i=1,10 do
            print(i)
            if i == 3 then
				print("co3 status: ", coroutine.status(co3))  
                print(coroutine.running()) 
            end
            coroutine.yield()
        end
    end
)
 
coroutine.resume(co3, 3) 
coroutine.resume(co3) 
coroutine.resume(co3) 
 
print(coroutine.status(co3))   
print(coroutine.running())
 



输出如下:

co1 status: 	suspended
1
co1 status: 	running
thread: 0x1206688	false
co1 status: 	dead
2
thread: 0x12053f8	false
3
1
2
3
co3 status: 	running
thread: 0x1205978	false
suspended
thread: 0x11fe018	true

当create一个coroutine的时候就是注册了一个新事件。当使用resume触发事件的时候,create的coroutine函数就被执行了,当遇到yield的时候就代表挂起当前线程,等候再次resume触发事件。



下面一个更细致的例子:

function foo (a)
    print("foo 函数输出", a)
    return coroutine.yield(2 * a) -- yield()的参数将会作为本次resume的返回值,而下次resume的入参将会成为本次yield的返回值。
end
 
co = coroutine.create(function (a , b)
    print("第一次协程执行输入", a, b) -- 1 10
    local r = foo(a + 1)
     
    print("第二次协程执行输入", r)
    local r, s = coroutine.yield(a + b, a - b)  
     
    print("第三次协程执行输入", r, s)
    return b, "结束协程"                   
end)
        
print("main", coroutine.resume(co, 1, 10)) -- true, 4
print("--分割线----")
print("main", coroutine.resume(co, "r")) -- true 11 -9
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 结束协程
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
print("---分割线---")
 

输出结果为:

第一次协程执行输入	1	10
foo 函数输出	2
main	true	4
--分割线----
r
第二次协程执行输入	r
main	true	11	-9
---分割线---
第三次协程执行输入	x	y
main	true	10	结束协程
---分割线---
main	false	cannot resume dead coroutine
---分割线---


来一个生产消费者的例子练手:

local producer = coroutine.create(
	function (num)
		local Num = 0
		repeat
			Num = Num + 1
			if num < 5 then
				num = num + 1
				print("producing...", num)
			else
				num = coroutine.yield(num)
				num = num + 1
				print("producing...", num)
			end
		until (Num >= 10)
		coroutine.yield(num)
	end
)

local consumer = coroutine.create(
	function (num)
		while true do
			if num > 0 then
				num = num - 1
				print("consuming...", num)
			else
				print("nothing to be consumed")
				num = coroutine.yield(num)
				print(num)
			end
		end
	end
)

function myFun()
	local num = 0
	_, num = coroutine.resume(consumer, num)
	_, num = coroutine.resume(producer, num)
	_, num = coroutine.resume(consumer, num)
	_, num = coroutine.resume(producer, num)
	_, num = coroutine.resume(consumer, num)
end

myFun()


输出如下:

nothing to be consumed
producing...	1
producing...	2
producing...	3
producing...	4
producing...	5
5
consuming...	4
consuming...	3
consuming...	2
consuming...	1
consuming...	0
nothing to be consumed
producing...	1
producing...	2
producing...	3
producing...	4
producing...	5
5
consuming...	4
consuming...	3
consuming...	2
consuming...	1
consuming...	0
nothing to be consumed


示例中一共生产了10次。每生产5个,就去消费。消费完了再生产。


resume和yield的配合强大之处在于,resume处于主程中(实际运用中resume不限于主程中),它将外部状态(数据)传入到协同程序内部;而yield则将内部的状态(数据)返回到主程中。


在SQL Server中,当你尝试将一个字符串或二进制数据插入到一个字段中,而该字段的长度不足以容纳整个数据时,会出现“将截断字符串或二进制数据”的错误。这个错误通常是由于以下几种原因引起的: 1. **字段长度不足**:你尝试插入的字符串长度超过了目标字段的定义长度。例如,目标字段定义为`VARCHAR(10)`,而你尝试插入一个长度为20的字符串。 2. **数据类型不匹配**:你尝试插入的数据类型与目标字段的数据类型不兼容。例如,目标字段是`INT`类型,而你尝试插入一个字符串。 3. **数据转换问题**:在数据插入过程中,数据类型转换导致了数据截断。例如,将`NVARCHAR`类型的数据插入到`VARCHAR`类型的字段中。 要解决这个问题,你可以采取以下几种措施: 1. **增加字段长度**:检查目标字段的定义长度,并根据需要增加长度。例如,将`VARCHAR(10)`改为`VARCHAR(50)`。 2. **检查数据类型**:确保插入的数据类型与目标字段的数据类型兼容。如果需要,进行适当的数据类型转换。 3. **数据验证**:在插入数据之前,验证数据的长度和类型,确保它们符合目标字段的定义。 例如,假设你有一个表`Employees`,其中有一个字段`Name`定义为`VARCHAR(10)`,而你尝试插入一个长度为20的字符串: ```sql INSERT INTO Employees (Name) VALUES ('ThisIsAVeryLongName'); ``` 这个操作会触发“将截断字符串或二进制数据”的错误。要解决这个问题,你可以增加`Name`字段的长度: ```sql ALTER TABLE Employees ALTER COLUMN Name VARCHAR(50); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值