python--asyncio模块
申明:本文章参考于https://www.jianshu.com/p/b5e347b3a17c
网络模型有很多种,为了实现高并发也有很多方案,多线程,多进程。无论多线程和多进程,IO的调度更多取决于系统,而协程的方式,调度来自用户,用户可以在函数中yield一个状态。使用协程可以实现高效的并发任务。Python的在3.4中引入了协程的概念,可是这个还是以生成器对象为基础,3.5则确定了协程的语法。下面将简单介绍asyncio的使用。实现协程的不仅仅是asyncio,tornado和gevent都实现了类似的功能。
event_loop 事件循环:程序开启一个无限的循环,程序员会把一些函数注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。 coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。 task 任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。 future: 代表将来执行或没有执行的任务的结果。它和task上没有本质的区别 async/await 关键字:python3.5 用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。 1.定义一个协程
# 如何定义一个协程:只需要在定义的函数前加上async关键字即可 import time import asyncio # 协程的定义可以通过async关键字来定义,当然定义之后的对象无法直接运行, # 类似于twisted,需要将其扔到事件循环当中 async def func(name): print(f'my name is {name}') st_time = time.time() coroutine = func('古明地盆') # 创建一个协程 loop = asyncio.get_event_loop() # 创建一个事件循环 loop.run_until_complete(coroutine) # 将要执行的协程注册到事件循环中,并启动。 print('用时:',time.time()-st_time)
输出结果如下
my name is 古明地盆 用时: 0.0
2.定义协程的另一种方式
import time import asyncio # 可以使用async关键字,也可以给函数添加一个装饰器,依旧可以把函数包装成一个协程 @asyncio.coroutine def func(name): print(f'my name is {name}') st_time = time.time() coroutine = func('古明地盆') loop = asyncio.get_event_loop() loop.run_until_complete(coroutine) print('用时:',time.time()-st_time) # asyncio.coroutine函数源码 def coroutine(func): """Decorator to mark coroutines. If the coroutine is not yielded from before it is destroyed, an error message is logged. """ if _inspect_iscoroutinefunction(func): # In Python 3.5 that's all we need to do for coroutines # defined with "async def". # Wrapping in CoroWrapper will happen via # 'sys.set_coroutine_wrapper' function. return func if inspect.isgeneratorfunction(func): coro = func else: @functools.wraps(func) def coro(*args, **kw): res = func(*args, **kw) if (base_futures.isfuture(res) or inspect.isgenerator(res) or isinstance(res, CoroWrapper)): res = yield from res elif _AwaitableABC is not None: # If 'func' returns an Awaitable (new in 3.5) we # want to run it. try: await_meth = res.__await__ except AttributeError: pass else: if isinstance(res, _AwaitableABC): res = yield from await_meth() return res if not _DEBUG: if _types_coroutine is None: wrapper = coro else: wrapper = _types_coroutine(coro) else: @functools.wraps(func) def wrapper(*args, **kwds): w = CoroWrapper(coro(*args, **kwds), func=func) if w._source_traceback: del w._source_traceback[-1] # Python < 3.5 does not implement __qualname__ # on generator objects, so we set it manually. # We use getattr as some callables (such as # functools.partial may lack __qualname__). w.__name__ = getattr(func, '__name__', None) w.__qualname__ = getattr(func, '__qualname__', None) return w wrapper._is_coroutine = _is_coroutine # For iscoroutinefunction(). return wrapper
输出结果如下
my name is 古明地盆 用时: 0.0
3.创建task任务
import time import asyncio # 协程对象并不能直接运行,需要的是task对象或future对象。 # 但在注册事件循环的时候,run_until_complete()函数会将协程包装成一个task对象 # task是future对象的子类,保存了协程运行后的状态,可以在未来的时候获取结果 # 所记不错的话,tornado4.0的时候也引入了future对象。future对象在tornado里面就类似于一个信使 # 穿梭于ioloop这个全局总调度器和协程之间 async def func(name): print(f'my name is {name}') st_time = time.time() coroutine = func('古明地盆') loop = asyncio.get_event_loop() task = loop.create_task(coroutine) print(task) loop.run_until_complete(task) print(task) print('用时:',time.time()-st_time)
输出结果如下
<Task pending coro=<func() running at C:/Users/Administrator/Desktop/完美世界/python--asyncio/3创建task任务.py:10>> my name is 古明地盆 <Task finished coro=<func() done, defined at C:/Users/Administrator/Desktop/完美世界/python--asyncio/3创建task任务.py:10> result=None> 用时: 0.0
为什么会输出上述结果,task在加入事件循环之前处于挂起状态,在加入事件循环执行完毕后,便处于了finished状态asyncio.ensure_future()和loop.create_task(),都可以创建一个task,run_until_complete()接收的参数是一个future对象当传入一个协程,内部会自动封装成一个task对象,task是future的子类.
print(isinstance(task,asyncio.Future)) # 打印结果为True
4.绑定回调
import time import asyncio # 绑定函数,在task执行完毕后可以获取执行的结果,回调的最后一个参数是future对象,通过该对象可以获取协程的返回值。 # 如果回调需要多个参数,可以使用python标准库functools下的partial偏函数导入 @asyncio.coroutine # 也可以用这种方式来定义一个协程 def func(name): print(f'my name is {name}') print('我要执行完毕了·····,下面我将指定一个白痴来接收我的返回值·····') return '多睡觉,少操心' def callback(future): print('我是白痴,开始接收返回值·····') print('收到返回值,打印:',future.result()) coroutine = func('古明地盆') loop = asyncio.get_event_loop() st_time = time.time() # 也可以使用future=asyncio.ensure_future(coroutine) # 也可以不写,如果不写,内部会自动包装成一个task对象,但此时就没办法使用回调 task = loop.create_task(coroutine) # 添加回调函数,当task任务执行完毕之后会自动执行 task.add_done_callback(callback) # 将包装好的task对象扔到事件循环当中 loop.run_until_complete(task) print('用时:',time.time()-st_time)
输出结果如下
my name is 古明地盆 我要执行完毕了·····,下面我将指定一个白痴来接收我的返回值····· 我是白痴,开始接收返回值····· 收到返回值,打印: 多睡觉,少操心 用时: 0.0
5.future与result
import asyncio import time # 回调编程一直是程序员的噩梦,包括我在内,程序员更喜欢用同步的方式编写异步代码,以避免回调的噩梦 # 回调中我们使用了future对象的result方法,在前面不绑定回调的栗子中,我们看到task有finished状态。在那个时候,可以直接读取task的result方法 @asyncio.coroutine def func(name): print(f'my name is {name}') time.sleep(3) return '多睡觉少操心' st_time = time.time() coroutine = func('古明地盆') loop = asyncio.get_event_loop() task = loop.create_task(coroutine) loop.run_until_complete(task) # 我们可以让程序睡三秒钟,发现在协程没有执行完毕的时候,task.result()处于阻塞状态,程序会卡在这里 # 在协程执行完毕,会获取到返回值并打印 print('task result',task.result()) print('用时:',time.time()-st_time)
输出结果如下
my name is 古明地盆 task result 多睡觉少操心 用时: 3.0127999782562256
6.阻塞与await
import time import asyncio # 使用async可以定义协程对象,使用await可以针对耗时的操作进行挂起,就想生成器里的yield一样,函数让出控制权。 # 协程遇到await,事件循环将会挂起该协程,执行别的协程,直到其他的协程也挂起或者执行完毕,再进行下一个协程的执行。 # 而一般耗时的主要是io操作,例如网络请求,文件读取等等,这些操作是不占用CPU的。我们可以使用asyncio.sleep()来模拟io操作 # 协程的目的也是使这些io操作异步化 # PS:所以这个模块才叫asyncio(异步io)呀~~~~~~~~ # 很重要的一点,之前我们说过讲一个函数变成协程有两种方式,其中一种是加上一个装饰器 # 由于这里使用了await关键,那么因此只能使用async关键字来定义一个协程。 # 至于为什么,是因为yield既可以作为生成器也可以当做协程。为了不引起歧义,python引入了async和await两个关键字 # 因此这两个关键字必须成对出现,并且yield不可以出现在async定义的协程当中 async def func(name): print(f'my name is {name}') await asyncio.sleep(3) return '多睡觉,少操心' st_time = time.time() coroutine = func('古明地盆') loop = asyncio.get_event_loop() task = loop.create_task(coroutine) loop.run_until_complete(task) print('TASK RESULT,',task.result()) print('用时:',time.time()-st_time)
输出结果如下
my name is 古明地盆 (隔了三秒钟) TASK RESULT, 多睡觉,少操心 用时: 3.0002057552337646
7.并发与并行
# 并发是指同一时间断有多个任务执行,但在同一时刻只有一个任务在实行 # 并行是指同一时刻有多个任务在执行 # asyncio实现并发,需要创建多个协程。每当任务阻塞的时候就await,然后其他协程继续工作。 # 创建多个协程的列表,将这些协程注册到事件循环中 import asyncio import time async def func(n): print(f'接下来等待{n}秒') await asyncio.sleep(n) return f'我等待了{n}秒' st_time = time.time() coroutine1 = func(1) coroutine2 = func(2) coroutine3 = func(4) loop = asyncio.get_event_loop() tasks = [ loop.create_task(coroutine1), loop.create_task(coroutine2), loop.create_task(coroutine3) ] # 也可以使用async.gather(*task),但是要将其拆成一堆task loop.run_until_complete(asyncio.wait(tasks)) for task in tasks: print('task result',task.result()) print('用时:',time.time()-st_time)
输出结果如下
接下来等待1秒 接下来等待2秒 接下来等待4秒 task result 我等待了1秒 task result 我等待了2秒 task result 我等待了4秒 用时: 4.004607677459717
# 值得注意的是,程序并不是一下子就将结果返回,比如等待一秒钟的那个协程,并不是等了一秒钟就打印结果# 而是等待所有协程都执行完毕之后再按照先后执行完毕的顺序一块打印
8.协程嵌套(1)
# 使用async定义协程,那么我们也可以在其内部封装更多的io操作,这样便实现了协程的嵌套 # 即在一个协程中await另一个协程,如此连接起来 import asyncio import time async def love_cat(t): print(f'等待{t}秒···') await asyncio.sleep(t) return f'等待了{t}秒' async def main(): coroutine1 = love_cat(1) coroutine2 = love_cat(2) coroutine3 = love_cat(4) tasks = [ asyncio.ensure_future(coroutine1), asyncio.ensure_future(coroutine2), asyncio.ensure_future(coroutine3), ] # 如果没有协程的嵌套,那么我们直接可以将tasks扔进事件循环当中 # 等待执行完毕,直接通过task.result() for task in tasks获取结果 # 但此时出现了协程的嵌套,位于协程的内部,无法通过task.result() for task in tasks获取结果 # 但注意,此时不要再尝试执行loop.run_until_complete(tasks) # 因为当外部的协程扔进事件循环当中,也可以执行内部的协程 # 直接通过await asyncio.wait(tasks)即可,会有两个返回值,一个已完成,一个未完成 dones,pending = await asyncio.wait(tasks) # 会有两个返回值,一个是已经完成,一个是未完成 # dones里面则包含了所有已经完成的task/future对象 for future in dones: print('TASK RESULT',future.result()) st_time = time.time() loop = asyncio.get_event_loop() task = loop.create_task(main()) loop.run_until_complete(task) print('用时:',time.time()-st_time)
输入结果如下
等待1秒··· 等待2秒··· 等待4秒··· TASK RESULT 等待了4秒 TASK RESULT 等待了1秒 TASK RESULT 等待了2秒 用时: 4.019207715988159
9.携程嵌套(2)
import asyncio import time async def love_cat(t): print(f'等待{t}秒···') await asyncio.sleep(t) return f'等待了{t}秒' async def main(): coroutine1 = love_cat(1) coroutine2 = love_cat(2) coroutine3 = love_cat(4) tasks = [ asyncio.ensure_future(coroutine1), asyncio.ensure_future(coroutine2), asyncio.ensure_future(coroutine3), ] # 可以直接返回await,让最外层的run_until_complete返回main协程的结果 return await asyncio.gather(*tasks) st_time = time.time() loop = asyncio.get_event_loop() task = loop.create_task(main()) results = loop.run_until_complete(task) for result in results: print('task result',result) print('用时:',time.time()-st_time)
输出结果如下
等待1秒··· 等待2秒··· 等待4秒··· task result 等待了1秒 task result 等待了2秒 task result 等待了4秒 用时: 4.013409852981567
10.协程嵌套(3)
import asyncio import time async def love_cat(t): print(f'等待{t}秒···') await asyncio.sleep(t) return f'等待了{t}秒' async def main(): coroutine1 = love_cat(1) coroutine2 = love_cat(2) coroutine3 = love_cat(4) tasks = [ asyncio.ensure_future(coroutine1), asyncio.ensure_future(coroutine2), asyncio.ensure_future(coroutine3), ] # 可以用asyncio.wait直接返回await,让最外层的run_until_complete返回main协程的结果 return await asyncio.wait(tasks) st_time = time.time() loop = asyncio.get_event_loop() task = loop.create_task(main()) dones,pending = loop.run_until_complete(task) for task in dones: print('task result',task.result()) print('用时:',time.time()-st_time)
输出结果如下
等待1秒··· 等待2秒··· 等待4秒··· task result 等待了1秒 task result 等待了2秒 task result 等待了4秒 用时: 4.014608144760132
11.协程嵌套(4)
import asyncio import time async def love_cat(t): print(f'等待{t}秒···') await asyncio.sleep(t) return f'等待了{t}秒' async def main(): coroutine1 = love_cat(1) coroutine2 = love_cat(2) coroutine3 = love_cat(4) tasks = [ asyncio.ensure_future(coroutine1), asyncio.ensure_future(coroutine2), asyncio.ensure_future(coroutine3), ] # 也可以使用asyncio.as_complete方法 for task in asyncio.as_completed(tasks): result = await task print('task result',result) st_time = time.time() loop = asyncio.get_event_loop() task = loop.create_task(main()) loop.run_until_complete(task)
输出结果如下
等待1秒··· 等待2秒··· 等待4秒··· task result 等待了1秒 task result 等待了2秒 task result 等待了4秒 # 这里打印返回值则不是一次性打印,而是谁先执行完,先返回谁。
12.取消协程(1)
# 上面提到的协程的用法,都是协程围绕着事件循环进行的操作。future有以下几种状态 # pending running done cancelled # 创建task的时候,此时还没有将其扔进循环队列,那么状态自然为pending(被挂起) # 事件循环调用执行的时候自然是running # 调用完毕自然就是done,此时可以通过result()方法获取返回值 # 如果需要停止事件循环那么需要把task取消 # 使用async定义协程,那么我们也可以在其内部封装更多的io操作,这样便实现了协程的嵌套 # 即在一个协程中await另一个协程,如此连接起来 import asyncio import time async def love_cat(t): print(f'等待{t}秒···') await asyncio.sleep(t) return f'等待了{t}秒' coroutine1 = love_cat(1) coroutine2 = love_cat(2) coroutine3 = love_cat(4) tasks = [ asyncio.ensure_future(coroutine1), asyncio.ensure_future(coroutine2), asyncio.ensure_future(coroutine3), ] st_time = time.time() loop = asyncio.get_event_loop() try: loop.run_until_complete(asyncio.wait(tasks)) except KeyboardInterrupt as e: print(asyncio.Task.all_tasks()) for task in asyncio.Task.all_tasks(): print(task.cancel()) loop.stop() loop.run_forever() finally: loop.close() print('用时:',time.time()-st_time) # 启动事件循环之后,马上按Ctrl+C,会触发run_until_complete的执行异常KeyBoardInterrupt # 然后通过循环asyncio.Task取消future
输出结果如下
等待1秒··· 等待2秒··· 等待4秒··· {<Task pending coro=<love_cat() running at 9协程停止.py:16> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x0000000002AB56A8>()]> cb=[_ wait.<locals>._on_completion() at C:\python36\lib\asyncio\tasks.py:380]>, <Task pending coro=<love_cat() running at 9协程停止.py:16> wait_for=<Future p ending cb=[<TaskWakeupMethWrapper object at 0x0000000002B5B828>()]> cb=[_wait.<locals>._on_completion() at C:\python36\lib\asyncio\tasks.py:380]>, <Tas k pending coro=<love_cat() running at 9协程停止.py:16> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x0000000002B5B888>()]> cb=[_wait. <locals>._on_completion() at C:\python36\lib\asyncio\tasks.py:380]>, <Task pending coro=<wait() running at C:\python36\lib\asyncio\tasks.py:313> wait_f or=<Future pending cb=[<TaskWakeupMethWrapper object at 0x0000000002B5B9A8>()]>>} True True True True 用时: 1.0154027938842773
# True表示cancel成功,loop循环在stop之后还需要再次开启循环,最后在close,否则还会引发异常 Task was destroyed but it is pending! task: <Task pending coro=<love_cat() done, defined at 9协程停止.py:14> wait_for=<Future cancelled> cb=[_wait.<locals>._on_completion() at C:\python36\l ib\asyncio\tasks.py:380]> Task was destroyed but it is pending! task: <Task pending coro=<love_cat() done, defined at 9协程停止.py:14> wait_for=<Future cancelled> cb=[_wait.<locals>._on_completion() at C:\python36\l ib\asyncio\tasks.py:380]> Task was destroyed but it is pending! task: <Task pending coro=<love_cat() done, defined at 9协程停止.py:14> wait_for=<Future cancelled> cb=[_wait.<locals>._on_completion() at C:\python36\l ib\asyncio\tasks.py:380]>
13.取消协程(2)
# 循环task,逐个cancel是一种方案,但像我们上面把task的列表封装在main函数中,m对main函数执行事件循环的调用 # 此时,main相当最外层的一个task,因此处理包装的main函数即可 import asyncio import time async def love_cat(t): print(f'等待{t}秒···') await asyncio.sleep(t) return f'等待了{t}秒' async def main(): coroutine1 = love_cat(1) coroutine2 = love_cat(2) coroutine3 = love_cat(4) tasks = [ asyncio.ensure_future(coroutine1), asyncio.ensure_future(coroutine2), asyncio.ensure_future(coroutine3), ] dones,pending = await asyncio.wait(tasks) for future in dones: print('task result',future.result()) st_time = time.time() loop = asyncio.get_event_loop() task = loop.create_task(main()) try: loop.run_until_complete(task) except KeyboardInterrupt as e: print(asyncio.Task.all_tasks()) print(asyncio.gather(*asyncio.Task.all_tasks()).cancel()) loop.stop() loop.run_forever() finally: loop.close() print('用时:',time.time()-st_time) # 不按Ctrl+C触发异常的话
输出结果如下
等待1秒··· 等待2秒··· 等待4秒··· task result 等待了1秒 task result 等待了2秒 task result 等待了4秒 用时: 4.014608144760132
# 触发异常 等待1秒··· 等待2秒··· 等待4秒··· {<Task pending coro=<main() running at 9协程停止1.py:20> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x0000000002B5B888>()]>>, <Task pending coro=<love_cat() running at 9协程停止1.py:8> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x0000000002B5B8E8>()]> cb=[_wait.<l ocals>._on_completion() at C:\python36\lib\asyncio\tasks.py:380]>, <Task pending coro=<love_cat() running at 9协程停止1.py:8> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x0000000002B5B948>()]> cb=[_wait.<locals>._on_completion() at C:\python36\lib\asyncio\tasks.py:380]>, <Task pendi ng coro=<love_cat() running at 9协程停止1.py:8> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x0000000002B5B9A8>()]> cb=[_wait.<locals >._on_completion() at C:\python36\lib\asyncio\tasks.py:380]>} True 用时: 1.0012037754058838
python--asyncio模块的更多相关文章
- Python asyncio 模块
Python 3.4 asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持. asyncio的编程模型就是一个消息循环.我们从asyncio模块中直接获取一个EventLo ...
- python——asyncio模块实现协程、异步编程
我们都知道,现在的服务器开发对于IO调度的优先级控制权已经不再依靠系统,都希望采用协程的方式实现高效的并发任务,如js.lua等在异步协程方面都做的很强大. Python在3.4版本也加入了协程的概念 ...
- Python - Asyncio模块实现的生产消费者模型
[原创]转载请注明作者Johnthegreat和本文链接 在设计模式中,生产消费者模型占有非常重要的地位,这个模型在现实世界中也有很多有意思的对应场景,比如做包子的人和吃包子的人,当两者速度不匹配时, ...
- Python标准模块--asyncio
1 模块简介 asyncio模块作为一个临时的库,在Python 3.4版本中加入.这意味着,asyncio模块可能做不到向后兼容甚至在后续的Python版本中被删除.根据Python官方文档,asy ...
- python协程--asyncio模块(基础并发测试)
在高并发的场景下,python提供了一个多线程的模块threading,但似乎这个模块并不近人如意,原因在于cpython本身的全局解析锁(GIL)问题,在一段时间片内实际上的执行是单线程的.同时还存 ...
- Python:asyncio模块学习
python asyncio 网络模型有很多中,为了实现高并发也有很多方案,多线程,多进程.无论多线程和多进程,IO的调度更多取决于系统,而协程的方式,调度来自用户,用户可以在函数中yield一个状态 ...
- Python之路(第四十七篇) 协程:greenlet模块\gevent模块\asyncio模块
一.协程介绍 协程:是单线程下的并发,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的. 协程相比于线程,最大的区别在于 ...
- Python之asyncio模块的使用
asyncio模块作用:构建协程并发应用的工具 python并发的三大内置模块,简单认识: .multiprocessing:多进程并发处理 .threading模块:多线程并发处理 .asyncio ...
- 后台程序处理 (一)python asyncio 协程使用
由于脚本需要在完成事件处理后N秒检查事件处理结果,当执行失败时再执行另一个事件处理. 想要最小化完成这个功能.同时在第一时间就将执行完毕的结果反馈给接口. 因此想到使用协程. 使用之前先翻阅了一下现有 ...
- python MultiProcessing模块进程间通信的解惑与回顾
这段时间沉迷MultiProcessing模块不能自拔,没办法,python的基础不太熟,因此就是在不断地遇到问题解决问题.之前学习asyncio模块学的一知半解,后来想起MultiProcessin ...
随机推荐
- TouTiao开源项目 分析笔记4==>一个简单APP 整体常用框架
1.效果预览 1.1.如下图所以,到目前为止所有的功能. 2.从InitApp开始->SplashActivity->MainActivity 2.1.InitApp源代码.这是整个项目的 ...
- ST-LINK JLINK JTAG SWD接线图
- android 文件下载 超简单
public void downloadPlug(String downloadUrl,String savePath) { try { URL url = new URL(downloadUrl); ...
- SDK接入注意点
1. 新建的android项目,要把MainActivity.java里生成的东西全部删去,最好只留个onCreate入口方法,不然会产生什么“hello world”,会把自己写的View内的东西覆 ...
- ASP.NET Core [2]:Middleware-请求管道的构成(笔记)
原文链接:http://www.cnblogs.com/RainingNight/p/middleware-in-asp-net-core.html 中间件处理请求主要分为三个阶段:1. 中间件的注册 ...
- corosync.conf
##totem定义集群内各节点间是如何通信的,totem本是一种协议,专用于corosync专用于各节点间的协议,协议是有版本的 totem { ##版本号 version: ##安全认证on|off ...
- python pyinstaller 打包程序报错解决
python打包exe,各种入坑 一.安装PyInstaller 1.安装pywin32 pip命令安装:pip install pywin32(推荐) 2.安装Pyinstaller pip命令安装 ...
- perror表
#define EPERM 1 /* Operation not permitted */ #define ENOENT 2 /* No such file or directory */ #defi ...
- LINQ to Entities 不识别方法“System.Guid Parse(System.String)”,因此该方法无法转换为存储表达式。
LINQ to Entities 不识别方法"System.Guid Parse(System.String)",因此该方法无法转换为存储表达式. linq 中不能转换类型
- Struts2的result返回类型