当前位置: 首页 > 面试题库 >

扭曲:为什么将延迟的回调传递给延迟的线程会导致该线程突然阻塞?

尤博达
2023-03-14
问题内容

我尝试使用txredis(redis的非阻塞扭曲api)作为持久性消息队列,但尝试将其与我正在研究的一个草率项目一起设置,但未成功。我发现,尽管客户端没有阻塞,但它变得比原先要慢得多,因为反应堆循环中原本应该成为一个事件的事件被分解为数千个步骤。

因此,我改为尝试使用redis-
py(常规阻止扭曲的api)并将调用包装在延迟线程中。它很好用,但是我想在调用redis时执行一次内部延迟,因为我想建立连接池以试图进一步加快速度。

以下是我对从扭曲文档中获取的一些示例代码的解释,这些示例代码用于递延线程以说明我的用例:

#!/usr/bin/env python
from twisted.internet import reactor,threads
from twisted.internet.task import LoopingCall
import time

def main_loop():
    print 'doing stuff in main loop.. do not block me!'


def aBlockingRedisCall():
    print 'doing lookup... this may take a while'
    time.sleep(10)
    return 'results from redis'

def result(res):
    print res

def main():
    lc = LoopingCall(main_loop)
    lc.start(2)
    d = threads.deferToThread(aBlockingRedisCall)
    d.addCallback(result)
    reactor.run()

if __name__=='__main__':
    main()

这是我对连接池的更改,该更改使延迟线程中的代码受阻:

#!/usr/bin/env python
from twisted.internet import reactor,defer
from twisted.internet.task import LoopingCall
import time

def main_loop():
    print 'doing stuff in main loop.. do not block me!'

def aBlockingRedisCall(x):
    if x<5: #all connections are busy, try later
        print '%s is less than 5, get a redis client later' % x
        x+=1
        d = defer.Deferred()
        d.addCallback(aBlockingRedisCall)
        reactor.callLater(1.0,d.callback,x)
        return d

    else: 
        print 'got a redis client; doing lookup.. this may take a while'
        time.sleep(10) # this is now blocking.. any ideas?
        d = defer.Deferred()
        d.addCallback(gotFinalResult)
        d.callback(x)
        return d

def gotFinalResult(x):
    return 'final result is %s' % x

def result(res):
    print res

def aBlockingMethod():
    print 'going to sleep...'
    time.sleep(10)
    print 'woke up'

def main():
    lc = LoopingCall(main_loop)
    lc.start(2)


    d = defer.Deferred()
    d.addCallback(aBlockingRedisCall)
    d.addCallback(result)
    reactor.callInThread(d.callback, 1)
    reactor.run()

if __name__=='__main__':
    main()

所以我的问题是,有人知道我的更改为什么会导致延迟的线程被阻塞和/或有人可以提出更好的解决方案吗?


问题答案:

好吧,正如扭曲的文档所说:

延迟不会使代码神奇地无法阻止

每当使用阻塞代码(例如)时sleep,都必须将其推迟到新线程。

#!/usr/bin/env python
from twisted.internet import reactor,defer, threads
from twisted.internet.task import LoopingCall
import time

def main_loop():
    print 'doing stuff in main loop.. do not block me!'

def aBlockingRedisCall(x):
    if x<5: #all connections are busy, try later
        print '%s is less than 5, get a redis client later' % x
        x+=1
        d = defer.Deferred()
        d.addCallback(aBlockingRedisCall)
        reactor.callLater(1.0,d.callback,x)
        return d

    else: 
        print 'got a redis client; doing lookup.. this may take a while'
        def getstuff( x ):
            time.sleep(3)
            return "stuff is %s" % x

        # getstuff is blocking, so you need to push it to a new thread
        d = threads.deferToThread(getstuff, x)
        d.addCallback(gotFinalResult)
        return d

def gotFinalResult(x):
    return 'final result is %s' % x

def result(res):
    print res

def aBlockingMethod():
    print 'going to sleep...'
    time.sleep(10)
    print 'woke up'

def main():
    lc = LoopingCall(main_loop)
    lc.start(2)


    d = defer.Deferred()
    d.addCallback(aBlockingRedisCall)
    d.addCallback(result)
    reactor.callInThread(d.callback, 1)
    reactor.run()

if __name__=='__main__':
    main()

如果redis api不太复杂,则可以使用twisted.web重写它,而不是仅在很多线程中调用阻塞的api,这样会更自然。



 类似资料:
  • 问题内容: 我有10个线程的线程组。 而且我看到所有10个线程是同时启动的,因此对于第一个Web请求同时创建不太可能的巨大负载。 我想以某种方式随机化开始时间。例如,在线程启动之间增加5秒延迟:第一个线程启动,然后5秒延迟,然后第二个线程启动,依此类推。 Ultimate Thread Group插件可以吗? 问题答案: 为什么不将Uniform_Random_Timerjmeter.apache

  • 问题内容: 我需要在循环中对数据库进行SQL查询: 更好的方法是:保持原样或循环后移动: 或者是其他东西 ? 问题答案: 整个要点是直到函数返回才执行,因此将其放置在要关闭的资源打开后的适当位置。但是,由于要在循环内创建资源,因此根本不要使用defer- 否则,在函数退出之前,您不会关闭在循环内创建的任何资源,因此它们会堆积直到然后。相反,您应该在每次循环迭代结束时关闭它们, 而无需 :

  • Flask 的设计原则中有一条是响应对象被创建并在一条可能的回调链中传递,而在 这条回调链但中的任意一个回调,您都可以修改或者替换掉他们。当请求开始被 处理时,还没有响应对象,响应对象将在这一过程中,被某个视图函数或者系统 的其他组件按照实际需要来闯将。 但是,如果您想在响应过程的结尾修改响应对象,但是这是对象还不存在,那么会发生 什么呢?一个常见的例子是您可能需要在 before-request

  • 描述 我有一个小应用程序,它使用hook更新状态,但每次更新时,都会导致页面延迟。我指的是实际的延迟,而不仅仅是“等待异步”延迟。 我的理论是,更新状态会重新呈现太多的组件,因为如果我将状态减少到更少的值,滞后就会消失。 从本质上说,我担心我更新状态的方式没有隔离我想要的值。 密码 我将回购加载到CodeSandbox:https://codesandbox.io/s/long-forest-y9

  • 我在java应用程序中使用log4j2。就日志功能而言,一切都很好,但是在启动应用程序时,它似乎会导致非常明显的延迟。 我已经把代码剥离到基本的 如果不包括log4j2,那么当我运行应用程序时,控制台中就会出现“TEST”,包括构建路径中的log4j2代码和文件夹,这会导致测试出现时间延迟40-50秒。 log4j2.xml中设置为跟踪的标志 谢谢你的帮助,亚历克西斯

  • 此应用程序收到 但是,当不存在web套接字会话,并且JMSProducer将消息发送到QueueSenderSessionBean中的目标“jms/notificationQueue”时,消息会立即在NotificationEndpoint中使用。这不是我的意图。 我的目的是让队列保留消息,直到用户连接到NotificationEndpoint。如果用户没有连接到NotificationEndpo