关于stackless python并发的疑惑

131 views
Skip to first unread message

Yul Chino

unread,
Jul 2, 2010, 4:19:44 AM7/2/10
to python-cn`CPyUG`华蟒用户组(中文Py用户组)
翻了下stackless的文档,在老帖中也找到篇关于并发性能的测试。
刚开始觉得很惊艳,根据[url]http://www.elias.cn/Develop/PyConcurrency[/url]中的代码
简单修改了一下,如下:
import stackless as SL

def run_benchmark(nm):
a_ch = SL.channel()
b_ch = SL.channel()
c_ch = SL.channel()
SL.tasklet(a)(a_ch, b_ch)
SL.tasklet(b)(b_ch, c_ch)
SL.tasklet(c)(c_ch)

for r in xrange(m-1, -1, -1):
a_ch.send(r)
SL.schedule()

def a(cin, cout):
while True:
r = cin.receive()
cout.send(r)
def b(cin, cout):
while True:
r = cin.receive()
cout.send(r)
def c(cin):
while True:
r = cin.receive()
用tiemit测了下结果:
D:\stepbystep>c:/Python26/python -m timeit -s "import test_stack"
"test_stack.ru
n_benchmark(10000)"
100 loops, best of 3: 17 msec per loop

而直接用顺序的话,代码如下:
import stackless as SL

def run_benchmark(n, m):
for r in xrange(m-1, -1, -1):
a(r)

def a(n):
b(n)
def b(n):
c(n)
def c(n):
pass

测试结果如下:
D:\stepbystep>c:/Python26/python -m timeit -s "import test_comm"
"test_comm.run_
benchmark(10000)"
100 loops, best of 3: 5.03 msec per loop

===========
额,这个结果与我所预想的似乎不一致(机器是双核的,应该支持并发的吧),号称的并发还不如线性的单进程单线程快。
我所能翻到的文档几乎都是将这个‘微线程’与线程作比较,而且都是大量生成微线程和线程,最终得出结论,线程数达到多少
就要崩溃了,或者线程间数据传递,切换慢了等。还没有找到说某个东东本来是单进程的,改成多个微线程配合则提高
效率之类的例子。想问,有这样的例子么?或者说,由于代码的问题并没有能够真正实现并发机制,那要如何修改呢?
个人感觉上面的代码写的倒更有点协程的味道,但是,这也不应该更慢才对啊。。望有大大指导下

PT M.

unread,
Jul 2, 2010, 4:32:31 AM7/2/10
to pyth...@googlegroups.com
stackless 的要解决问题的典型例子是

def func_a():
    #do something
    b()

def func_b():
    #do something
    a()

你的代码经过了schedule的调度,复杂度肯定比几个切换栈操作复杂,慢是正常的;

2010/7/2 Yul Chino <yuan...@gmail.com>

--
来自: `python-cn`:CPyUG ~ 华蟒用户组 | 发言:pyth...@googlegroups.com
退订: http://tinyurl.com/45a9tb //针对163/qq邮箱:http://tinyurl.com/4dg6hc
详情: https://groups.google.com/group/python-cn
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp



--
Arch Linuxer, Pythoner, Geek
--> Blog: http://apt-blog.net

PT M.

unread,
Jul 2, 2010, 4:33:16 AM7/2/10
to pyth...@googlegroups.com

def func_a():
    #do something
    func_b()

def func_b():
    #do something
    func_a()

刚为了好看, 敲错了

2010/7/2 PT M. <pen...@gmail.com>

Yul Chino

unread,
Jul 2, 2010, 4:59:09 AM7/2/10
to python-cn`CPyUG`华蟒用户组(中文Py用户组)

On 7月2日, 下午12时33分, "PT M." <pen...@gmail.com> wrote:
> def func_a():
> #do something
> func_b()
>
> def func_b():
> #do something
> func_a()
>

额,这个可不可以理解成stackless只是在' 多个节点之间互相捣腾 '时有效率上的提高,而节点间的顺序传递就没有效果了?
还有我代码中的这个 schedule可以删掉的,效果一样(还是那么慢)

------
补充一点,一般在多个节点间传递处理数据,如果有节点会有一些类似io之类可能要等待的操作时可以用多线程来解决,这个时候"微线程"似乎也没有效

PT M.

unread,
Jul 2, 2010, 5:04:22 AM7/2/10
to pyth...@googlegroups.com
stackless有篇tutorial的例子是模拟一圈人踢毽子,对比系统线程跟微线程,前者在开到几千时候系统就吃不消了,微线程开到十万依然稳定,内存也很小。

io等待可以用stackless啊

2010/7/2 Yul Chino <yuan...@gmail.com>

--
来自: `python-cn`:CPyUG ~ 华蟒用户组 | 发言:pyth...@googlegroups.com
退订: http://tinyurl.com/45a9tb //针对163/qq邮箱:http://tinyurl.com/4dg6hc
详情: https://groups.google.com/group/python-cn
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp

Yul Chino

unread,
Jul 2, 2010, 6:17:22 AM7/2/10
to python-cn`CPyUG`华蟒用户组(中文Py用户组)

On 7月2日, 下午1时04分, "PT M." <pen...@gmail.com> wrote:
> stackless有篇tutorial的例子是模拟一圈人踢毽子,对比系统线程跟微线程,前者在开到几千时候系统就吃不消了,微线程开到十万依然稳定,内存也 很小。

这个例子我看过,就是我说的那种开了一堆 微线程 和 线程 做比较的,不是我想要的

> io等待可以用stackless啊

我做了这么个测试,代码如下:
import urllib
'''单进程'''
def main():
urls =
['www.shopex.cn','www.ibm.com','www.apple.com','www.oracle.com']
for url in urls:
a(url)

def a(url):
url = "http://%s"%url
b(url)
def b(url):
urllib.urlopen(url)
测试结果:
[root@localhost tmp]# python -m timeit -s "import test_comm"
"test_comm.main()"
10 loops, best of 3: 2.46 sec per loop
代码:
'''多线程'''
import urllib
import threading,Queue

class T(threading.Thread):
def __init__(self,queue):
threading.Thread.__init__(self)
self.queue = queue

def run(self):
url = self.queue.get()
urllib.urlopen(url)
self.queue.task_done()

def main():
global queue
queue = Queue.Queue()
t = T(queue)
t.setDaemon(True)
t.start()
urls =
['www.shopex.cn','www.ibm.com','www.apple.com','www.oracle.com']

for url in urls:
a(url)

def a(url):
global queue
url = "http://%s"%url
queue.put(url)
测试结果:
[root@localhost tmp]# python -m timeit -s "import test_thread"
"test_thread.main()"
1000 loops, best of 3: 4.9 msec per loop

代码:
'''stackless'''
import urllib
import stackless

def main():
urls =
['www.shopex.cn','www.ibm.com','www.apple.com','www.oracle.com']
a_ch = stackless.channel()
b_ch = stackless.channel()
stackless.tasklet(a)(a_ch, b_ch)
stackless.tasklet(b)(b_ch)

for url in urls:
a_ch.send(url)

def a(cin, cout):
while True:

url = cin.receive()
url = "http://%s"%url
cout.send(url)
def b(cin):
while True:
url = cin.receive()
urllib.urlopen(url)
测试结果:
[root@localhost tmp]# python -m timeit -s "import test_stack"
"test_stack.main()"
10 loops, best of 3: 2.43 sec per loop

=========
很明显能够看出 多线程 策略 的效率要高的多,而微线程情何以堪啊。抑或,我这儿没有处理好?

ALENS(蓝)

unread,
Jul 2, 2010, 10:55:58 AM7/2/10
to pyth...@googlegroups.com
STACKLESS好像没异步的操作,不知道你的问题是不是出这呢,如果你想异步访问的话,下个STACKLESSSOCKET这包来试下吧

2010/7/2 Yul Chino <yuan...@gmail.com>



--

seewind

unread,
Jul 2, 2010, 11:05:04 AM7/2/10
to python-cn`CPyUG`华蟒用户组(中文Py用户组)
呵呵。。。看你的stackless代码,就知道你还不了解stacless。
1、stackless是协程,就是跑起来其实是单线程执行的;
2、你的例子中,其实没实际用到stackless的优点,就是io没有异步化,具体修改,下载stacklesssocket.py,在你的测试代码
头加上:
import stacklesssocket
stacklesssocket.install()
3、协程的好处,在于能创建很多很多,这样能以简单多线程编程方式开发超多并发的应用;至于利用多核,可以结合真实线程和多进程。

Yul Chino

unread,
Jul 2, 2010, 12:04:45 PM7/2/10
to python-cn`CPyUG`华蟒用户组(中文Py用户组)
On 7月2日, 下午7时05分, seewind <seewin...@gmail.com> wrote:
> 呵呵。。。看你的stackless代码,就知道你还不了解stacless。
> 1、stackless是协程,就是跑起来其实是单线程执行的;

好吧,我承认自己真的还不算了解stackless,目前的理解全部都建立在《stackless python并发编程》中文译本上。
里面的一些术语还让我蛮疑惑的,它里面细分了 '微进程' '协程' '轻线程'。总的感觉似乎都差不多,所以就依葫芦画瓢了。。


> 3、协程的好处,在于能创建很多很多,这样能以简单多线程编程方式开发超多并发的应用;至于利用多核,可以结合真实线程和多进程。
>

这个协程,建很多很多和并发有什么关系呢?这里给个简单场景:
def a(data):pass
def b(data):pass
def c(data):pass
if __name__=='__main__':
for data in datas:
data = a(data)
data = b(data)
c(data)
应该怎样改造才能体现出协程的好处,使得效率比以上模式更高呢?

MuSheng

unread,
Jul 3, 2010, 1:50:53 AM7/3/10
to pyth...@googlegroups.com

我對“微進程”、“協程”、“輕線程”了解也不是很透徹,希望大牛能解釋一下,我都
將它們看成一個東西,就叫”協程“吧。

”協程“、”線程“、”進程“三者所在的空間不一樣,”協程“只能在同一線程中切
換,”線程“只能在同一進程中交換。”進程“就不用說了,各個進程都有一個獨立的
內存空間。
這樣也就決定三者對資源的開銷不一樣,協程<線程<進程,如果頻繁進行創建-銷
毀動作,除了內存外,還可以看到三者對創建-銷毀所花的時間不一樣。

另外再說一下“並發”,不知道Yul說的“並發”是指哪個層面的。IO的並發處理?還
是指令的並發執行?還是任務的並發處理?
照我的理解大數情況說的並發是指IO或任務的並發處理。從物理層面來說只有多進
程、多CPU情況下才可能實現指令的並發處理。大多數的並發、多任務都是你用一
會我用一會來實現,至於什麼時候給誰用,這個大多數情況下都是操作系統來作調度。

協程,就是在一個線程內可以自己對任務進行調度,只有涉及到IO等待情況下協程
才會體現優勢。就用下面greenlet協程例子作下說明吧。

buffer=[]

def writetobuffer(io):
#每次從io讀取1024個字節數據到buffer中
#讀取後切換到readfrombuffer處理數據
while not io.close():
buffer.append(io.read(1024)) #建議使用非阻塞方式讀取io
readfrombuffer.switch()
buffer.append('')

def readfrombuffer():
while True:
if buffer:
d=buffer.pop(0)
if d=='':
break
print d
writetobuffer.switch()

if __name__=='__main__':
a=greenlet(writetobuffer)
b=greenlet(readfrombuffer)
a.switch()


我這樣的理解不知道對不對,坐等大牛拍磚。

Shell Xu

unread,
Jul 3, 2010, 2:55:16 PM7/3/10
to pyth...@googlegroups.com
我解释一下限定于linux系统上的进程,线程,协程的本质吧。
进程是系统资源调度的单位,古典的,他也是CPU调度的单位。
现在,CPU调度的单位是线程。当CPU需要找到下一个需要被分配时间片的对象时,他找的是活跃线程链表。
进程和线程的区别在于,多进程的句柄和内存空间都是分离的,因此某个进程崩溃也不会殃及其他进程,而线程就不是这样了。
不过由于要分离句柄和内存空间,所以进程的开销略略大于线程。
好了,现在是重头戏,协程是什么?
协程是内存数据结构。而进程和线程不是,他们是完整的多CPU利用机制。
当CPU完成切换时——当然,我说的是80x86系统——他使用某个机制保存当前的上下文。就是将寄存器中的所有值全部倒出到一个内存区域,并且载入另一个内存区域保存的值。
一旦全部寄存器全部倒出,自然会停止在当前位置的执行,转入另一个执行状态。我们会有不同的ECS:EIP,堆栈地址,于是我们就可以看作是有不同的上下文了。
进程切换是在操作系统的帮助下完成的,因为需要ring0来完成上下文切换。所以你无法预料什么时候切换,导致你需要锁。
例如以下动作:
i+=1;
实际上,是这样的:
mov ax, ds:[bx+0xc]
inc ax
mov ds:[bx+0xc], ax
当然,代码不一定对。不过如果系统在你完成1或者2的时候进行调度,并且其他人对i修改了很多。当你切换回来的时候,你会将i的值不顾一切的改回来,哪怕这个系统只有一个CPU。这个现象就叫做线程非安全。
由于线程又需要系统支持,又不安全,因此导致在脚本语言中,经常有另一套方法来实现线程,叫做有限状态机。
在脚本语言中,保存你的状态,并不需要倒出寄存器,因为你的状态就是内存中的一组数据。很抱歉我没法详细描述这个,只有写过VM解释器才真正能理解这个东西实质是怎么回事。
VM中,代码执行到哪里是个数据,现在的命名空间结构是个数据,堆栈也是个数据——另说一句,我曾经用过python的list来模拟系统栈,效率很低。
当你在一个协程中,切换到另一个协程的时候,当前协程对象保存你的VM内部状态,目标协程对象将它的数据恢复到VM内部——于是你上下文切换了。
这有两个好处,首先是不需要锁了——协程切换只发生在必要的时候,只要你精确控制,不在上文中这种变态位置进行上下文切换,就没有锁定问题。
其次,协程的切换成本比较低——因为我们不需要像转盘子那样换来换去,我们只在必要的时候切换,因此次数非常少。
协程的本质是单线程。
这导致协程对SMP的利用是非完全的,因此需要和多进程互补,来充分利用SMP。
拿协程和单线程比是无比傻的决定,本来就单线程的事情何必费劲要协程做?
协程的比较目标是多线程。
当然,技术上说,我们可以把很多多线程程序拆成单线程的,例如代码1执行了一遍,就转到大调度结构上,看看哪个代码可以继续执行——耳熟?select模型?
但是这个技术无法解决上下文的问题,我们必须把某个线程有关的东西全部放到某个对象里面去。例如我们在进行树形搜索,半途需要请求一个数据的时候,你要自行记录这个树的搜索状态,以便下次恢复。因为一旦执行代码切换,堆栈就不是你的了。
所以更简单的方法就是找专家来干——协程。
C下面要干这个,需要用setcontext什么的库。

--
来自: `python-cn`:CPyUG ~ 华蟒用户组 | 发言:pyth...@googlegroups.com
退订: http://tinyurl.com/45a9tb //针对163/qq邮箱:http://tinyurl.com/4dg6hc
详情: https://groups.google.com/group/python-cn
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp



--
无能者无所求,饱食而遨游,泛若不系之舟

Yul Chino

unread,
Jul 4, 2010, 5:48:07 AM7/4/10
to python-cn`CPyUG`华蟒用户组(中文Py用户组)
On 7月3日, 下午10时55分, Shell Xu <shell909...@gmail.com> wrote:
> 我解释一下限定于linux系统上的进程,线程,协程的本质吧。
先要谢谢Shell Xu,从根本上解释了这堆东东(额,我大致是理解了,呵呵)

> 拿协程和单线程比是无比傻的决定,本来就单线程的事情何必费劲要协程做?
> 协程的比较目标是多线程。
个人觉得无论是线程还是协程都是为了提高整个程序的运行速度/效率而运用的一种技巧/手段。
以我之前给的场景:
def a(data):pass
def b(data):pass#有IO等待或其他耗时的操作


def c(data):pass
if __name__=='__main__':
for data in datas:
data = a(data)
data = b(data)
c(data)

这个用多线程效率是远比单线程高的。
那么既然协程的对比目标是多线程,那么要如何修改才能体现出协程的独到高效之处?小子其实最想知道的是这个,呵呵

seewind

unread,
Jul 4, 2010, 7:37:49 AM7/4/10
to python-cn`CPyUG`华蟒用户组(中文Py用户组)
线程和协程不能简单比执行效率,哪个高,哪个好。协程开发模式,在某些特定场合很好用。
stackless的作者说过,使用stackless,"受益最大的那些特定应用程序可能是 Swarm 仿真,或有许多角色扮演极小任务的多人游
戏。"
在这样的应用场景下,协程比多线程好用。看eve-online,同时上万人在线游戏,每一个人一个线程去仿真,线程作不到。
再看看高并发的web chat,上万人进入聊天室聊天,协程开发模式是不是很合适。:)

阿暖

unread,
Jul 4, 2010, 10:33:37 AM7/4/10
to pyth...@googlegroups.com
for data in datas:
data = a(data)
data = b(data)
c(data)

???这个例子里 a b c不可分吧

诚子

unread,
Jul 4, 2010, 10:40:05 AM7/4/10
to pyth...@googlegroups.com

shell

unread,
Jul 4, 2010, 4:25:49 PM7/4/10
to pyth...@googlegroups.com
�� 2010��07��04�� 13:48, Yul Chino �:
> On 7��3��, ����10ʱ55��, Shell Xu <shell909...@gmail.com> wrote:
>
>> �ҽ���һ���޶���linuxϵͳ�ϵĽ�̣��̣߳�Э�̵ı��ʰɡ�
>>
> ��ҪллShell Xu,�Ӹ��Ͻ�������Ѷ���(��Ҵ���������ˣ��Ǻ�)
>
>
>> ��Э�̺͵��̱߳����ޱ�ɵ�ľ����������͵��̵߳�����αطѾ�ҪЭ������
>> Э�̵ıȽ�Ŀ���Ƕ��̡߳�
>>
> ���˾����������̻߳���Э�̶���Ϊ������������������ٶ�/Ч�ʶ����õ�һ�ּ���/�ֶΡ�
> ����֮ǰ��ij�����
> def a(data):pass
> def b(data):pass#��IO�ȴ�������ʱ�IJ���

> def c(data):pass
> if __name__=='__main__':
> for data in datas:
> data = a(data)
> data = b(data)
> c(data)
> ����ö��߳�Ч����Զ�ȵ��̸߳ߵġ�
> ��ô��ȻЭ�̵ĶԱ�Ŀ���Ƕ��̣߳���ôҪ����޸IJ������ֳ�Э�̵Ķ�����Ч֮����С����ʵ����֪������������Ǻ�
>
>
ֻ��˵Ϊ�˽Ŷ�ר�����һ��Ь�ӵģ�û��˵ר��Ϊ��һ˫Ь�Ӹ���������ġ�

老光

unread,
Jul 5, 2010, 1:42:36 AM7/5/10
to pyth...@googlegroups.com
感觉楼主所举的问题是一个生产流水线的问题,a,b,c分别是三个步骤.
如果对产出结果的顺序没有要求的话,可以设两个管道文件来存放中间结果.abc可以同步运行.

不过,若事实是,总是b花的时间多,而不是abc随机花的时间多,那这样的修改对于总时间不会有很大提高,只是可能节约a,c花的时间而已.

----- Original Message -----
From: "shell" <shell...@gmail.com>
To: <pyth...@googlegroups.com>
Sent: Monday, July 05, 2010 12:25 AM
Subject: Re: [CPyUG] Re: 关于stackless python并发的疑惑


>于 2010年07月04日 13:48, Yul Chino 写道:
>> On 7月3日, 下午10时55分, Shell Xu <shell909...@gmail.com> wrote:
>>
>>> 我解释一下限定于linux系统上的进程,线程,协程的本质吧。
>>>
>> 先要谢谢Shell Xu,从根本上解释了这堆东东(额,我大致是理解了,呵呵)
>>
>>
>>> 拿协程和单线程比是无比傻的决定,本来就单线程的事情何必费劲要协程做?
>>> 协程的比较目标是多线程。
>>>
>> 个人觉得无论是线程还是协程都是为了提高整个程序的运行速度/效率而运用的一种技巧/手段。
>> 以我之前给的场景:
>> def a(data):pass
>> def b(data):pass#有IO等待或其他耗时的操作
>> def c(data):pass
>> if __name__=='__main__':
>> for data in datas:
>> data = a(data)
>> data = b(data)
>> c(data)
>> 这个用多线程效率是远比单线程高的。
>> 那么既然协程的对比目标是多线程,那么要如何修改才能体现出协程的独到高效之处?小子其实最想知道的是这个,呵呵
>>
>>
> 只听说为了脚而专门设计一个鞋子的,没听说专门为了一双鞋子给脚做手术的。

Yul Chino

unread,
Jul 5, 2010, 3:32:49 AM7/5/10
to python-cn`CPyUG`华蟒用户组(中文Py用户组)

On 7月5日, 上午9时42分, 老光 <yaoguangm...@cq.chinatelecom.com.cn> wrote:
> 感觉楼主所举的问题是一个生产流水线的问题,a,b,c分别是三个步骤.

对头!就是个流水线的问题。

> 如果对产出结果的顺序没有要求的话,可以设两个管道文件来存放中间结果.abc可以同步运行.

没有产出结果的顺序要求。至于设管道文件同步运行,是否就是我一开始写的"微线程or协程"方式?

> 不过,若事实是,总是b花的时间多,而不是abc随机花的时间多,那这样的修改对于总时间不会有很大提高,只是可能节约a,c花的时间而已.

实际项目上abc的时间是浮动的。所以对调高效率是可以预期的。本来就是准备用多进程/多线程 并发 来解决。因为上次 上海聚会 有提到
协程的概念,所以想尝试下。于是便有了这一堆东西,可惜似乎离预想的比较远。

不知道有没有大牛用协程/微线程 实现 类似此类 流水线似的的并发,望指教

Yul Chino

unread,
Jul 5, 2010, 5:29:18 AM7/5/10
to python-cn`CPyUG`华蟒用户组(中文Py用户组)

> ֻ ˵Ϊ ˽Ŷ ר һ Ь ӵģ û ˵ר Ϊ һ˫Ь Ӹ ġ

不是太理解。现在我的程序"脚"已经有了(可以正常运行了),我想让它跑的更快更稳,于是想做双鞋子。
本来是选定用革质的(多线程/多进程)的料子,后来听说皮质的(协程/微线程)不错。于是尝试着做了个
模型(主贴),发现穿上去似乎没有传说中的那么好。怀疑是自己的工艺问题(或者胶质配备不合理等原因)导致的,
所以想咨询下相应的专业人士有没有整改的建议,或者说,皮质的就不适合做这类的鞋子。并没有说为了使用
"协程"这双跑鞋就硬要把脚往里挤。

Shell Xu

unread,
Jul 5, 2010, 9:52:45 AM7/5/10
to pyth...@googlegroups.com
可是我完全没看到你的脚,我就看到一只蜈蚣和一只蚯蚓比谁跑的快。。。
主贴中的代码完全属于没事找事型,协程的设计目标是帮助你在阻塞的时候保存上下文状态。你主贴里面起码写个哪里有阻塞啊。


--
来自: `python-cn`:CPyUG ~ 华蟒用户组 | 发言:pyth...@googlegroups.com
退订: http://tinyurl.com/45a9tb //针对163/qq邮箱:http://tinyurl.com/4dg6hc
详情: https://groups.google.com/group/python-cn
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp



--
无能者无所求,饱食而遨游,泛若不系之舟

Yul Chino

unread,
Jul 5, 2010, 10:47:49 AM7/5/10
to python-cn`CPyUG`华蟒用户组(中文Py用户组)
On 7月5日, 下午5时52分, Shell Xu <shell909...@gmail.com> wrote:
> 可是我完全没看到你的脚,我就看到一只蜈蚣和一只蚯蚓比谁跑的快。。。
> 主贴中的代码完全属于没事找事型,协程的设计目标是帮助你在阻塞的时候保存上下文状态。你主贴里面起码写个哪里有阻塞啊。
>
额。。这个得声明一下,我的确是在找事,但却不是没事闲着慌。找事是为了寻找碰撞。
我对协程的理解真的连半桶水都算不上,不过如果shell老兄愿意点一下主贴中给的链接就会很轻易的发现我模仿的就是elias
的测试案例"有n个节点,传送m条数据"。当然了,我尽自己的理解力(或许错了吧)稍微简化了一下,并且改成了单线程版进行对比,得
出了一些数据,未必正确,但是由此引出了一些疑惑而已。
在shell老大的第一份回帖--"对协程的释疑"中,我已经给了回复,并且给出了阻塞的节点,或许老大忙碌没有瞅见。在与其他大大的回帖
中,大致也理出了一个大概。
好吧,既然泛泛的说法会引起不必要的误会,那么设个详细的场景如下:
在一个流水线的作业中有a,b,c三个节点,分别对数据做部分处理,三个节点的执行顺序是单向的:a->b->c
def a(msg):
'对数据进行处理,整理出url'
def b(url):
'根据url去网络上爬取数据'
def c(data):
'将爬取到的数据写入数据库'
def main():
for msg in msgs:
url = a(msg)
data = b(url)
c(data)
OK,如此一来,应该不会有什么误解了吧。很明显的,如果把b和c节点抽出来改成多线程/线程池,效率提高是可以预见的。
我原来的想法是用微线程/协程 替代或部分替代 线程来实现并发(或者是其他的什么称谓,反正目的就是提高效率)。可惜
如主贴中那般改法是没有取到预期效果,于是乎就有了这篇帖子。
shell老大应该可以理解这种 得门而不入 的感觉吧,望指教~

Shell Xu

unread,
Jul 5, 2010, 3:43:44 PM7/5/10
to pyth...@googlegroups.com
通常而言,我这么干。
def a(blah): 和你的一样
def b(blah): 和你的一样
def c(blah): 和你的一样
def thread_main(msg): c(b(a(msg)))
def main():
    global grlist
    for msg in msgs:
        gr = greenlet(msg)
        gr.start()
        grlist.append(gr)
OK,直到这里,协程还是一点优势都没有的。
问题是,你必须自行找出,什么时候应该离开当前协程。以及什么时候应回到当前协程。
以你的例子为例,你要找到办法,去检测某个协程中的url下载动作什么时候结束。代码就会变成类似这个样子。
def b(url):
    data = non-block.download(url)
    getone(grlist).switch()
    return data.getvalue()
当然,事情不会这么简单。当你需要的tcp数据量大于窗口的时候,这么干很可能会锁死这个协程,永远回不来。但是可以实现(并且有非常好的实现)一个http下载库,可以和协程一起工作,基本原理全是——select。
我们可以简单的考虑一下,你有100个job。当第一个去下载的时候,第二个开始准备,二开始下载的时候,又切换开始了第三个。等所有准备完成了,第一个也下载好了,可以开始继续处理了。这种切换法只进行了200次切换(准确说是199次),低开销,而且不用锁。
问题是,很不幸。正如我上面说的,协程是单线程。这个方案无法支持SMP,除非你用多进程再来一次。不过你都用多进程了,还用协程干嘛呢?
你的问题的本质是利用多核CPU,正解是多进程,精确点说是进程池。即使你用了协程,还是要用多进程进行包装的。
如果你需要切进来多次,接受数据,处理一下,再进入阻塞,这才是利用协程的地方。当然,为了充分利用多核CPU,你还是得用多进程包装一次的。

协程的经典问题是长连接http服务器。


--
来自: `python-cn`:CPyUG ~ 华蟒用户组 | 发言:pyth...@googlegroups.com
退订: http://tinyurl.com/45a9tb //针对163/qq邮箱:http://tinyurl.com/4dg6hc
详情: https://groups.google.com/group/python-cn
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp



--
无能者无所求,饱食而遨游,泛若不系之舟

Yul Chino

unread,
Jul 6, 2010, 12:59:10 PM7/6/10
to python-cn`CPyUG`华蟒用户组(中文Py用户组)
> 问题是,很不幸。正如我上面说的,协程是单线程。这个方案无法支持SMP,除非你用多进程再来一次。不过你都用多进程了,还用协程干嘛呢?
要的就是这个嗮,团队中的每个个体都更健壮了,团队自然更强大了(咔咔,这逻辑应该没问题吧)

> 如果你需要切进来多次,接受数据,处理一下,再进入阻塞,这才是利用协程的地方。当然,为了充分利用多核CPU,你还是得用多进程包装一次的。
> 协程的经典问题是长连接http服务器。

似乎抓到点了!这个长连接是不是非阻塞式的?
def main():
global grlist
while True:
msg = socket.get()#长连接


gr = greenlet(msg)
gr.start()
grlist.append(gr)

是不是这个socket的链接做成非阻塞的,就能发挥出协程的能量了?也只有这种类似的非阻塞式长链接模式才能淋漓的体现协程的力量?

Shell Xu

unread,
Jul 6, 2010, 2:41:17 PM7/6/10
to pyth...@googlegroups.com
没错,必须是非阻塞的。
协程的要点是,不会做无谓的调度。除非你自行switch,否则不出现上下文切换。如果是阻塞型的调用,就会将全部CPU阻塞在这个协程上。因此,协程的使用必须是永不阻塞的。
沈崴有句话,如果你的架构使得压力仅在CPU上,那就是个好架构。


--
来自: `python-cn`:CPyUG ~ 华蟒用户组 | 发言:pyth...@googlegroups.com
退订: http://tinyurl.com/45a9tb //针对163/qq邮箱:http://tinyurl.com/4dg6hc
详情: https://groups.google.com/group/python-cn
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp



--
无能者无所求,饱食而遨游,泛若不系之舟

zhao shichen

unread,
Jul 15, 2010, 7:08:28 AM7/15/10
to pyth...@googlegroups.com
我理解的是stackless解决了event驱动的程序开发的问题。

提供了一种方便的设计模式。

对于多核cpu一点用处也没有。
Reply all
Reply to author
Forward
0 new messages