如何同步两个交替的线程

时间:2015-12-15 21:25:02

标签: python multithreading locking

我需要启动两个线程,控制哪个线程首先启动,然后让它们交替工作。

以下代码与do_sleep = True按预期方式有效,但do_sleep = False可能失败。

如果不使用那些丑陋(且不可靠)的睡眠,我怎样才能达到相同的效果?

它与do_sleep = True一起使用的原因是:

  • 在尝试获取锁定并启动下一个作业之前,每个工作线程都会给另一个线程启动时间
  • 在第一个和第二个工作人员的开始之间有一个暂停,允许第一个工作人员在第二个工作准备就绪之前获得锁定

使用do_sleep = False可能会失败,因为:

  • 在每个作业结束时,每个线程可以尝试在另一个线程之前获取下一个周期的锁,执行两个连续的作业而不是交替
  • 第二个主题可以在第一个主题之前获得锁定

以下是代码:

import threading
import time
import random

do_sleep = True

def workerA(lock):
    for i in range(5):
        lock.acquire()
        print('Working A - %s' % i)
        time.sleep(random.uniform(0.2, 1))
        lock.release()
        if do_sleep: time.sleep(0.1)

def workerB(lock):
    for i in range(5):
        if do_sleep: time.sleep(0.1)
        lock.acquire()
        print('Working B - %s' % i)
        time.sleep(random.uniform(0.2, 1))
        lock.release()
        if do_sleep: time.sleep(0.1)

lock = threading.Lock()

t1 = threading.Thread(target=workerA, args=(lock, ))
t2 = threading.Thread(target=workerB, args=(lock, ))

t1.start()
if do_sleep: time.sleep(0.1)
t2.start()

t1.join()
t2.join()

print('done')

修改 使用Mike建议的Queue没有帮助,因为第一个工作人员无需等待第二个工作即可完成工作。

使用Lock替换Queue后,这是版本的错误输出:

Working A - 0
Working A - 1
Working B - 0
Working A - 2
Working B - 1
Working A - 3
Working B - 2
Working A - 4
Working B - 3
Working B - 4
done

这是使用do_sleep = False获得的错误输出:

Working A - 0
Working A - 1
Working A - 2
Working A - 3
Working A - 4
Working B - 0
Working B - 1
Working B - 2
Working B - 3
Working B - 4
done

这是使用do_sleep = True获得的正确输出:

Working A - 0
Working B - 0
Working A - 1
Working B - 1
Working A - 2
Working B - 2
Working A - 3
Working B - 3
Working A - 4
Working B - 4
done

2 个答案:

答案 0 :(得分:2)

解决这个问题的几种方法。一个相对简单的方法是使用锁来控制对单独共享变量的访问:调用另一个变量owner,它可以设置为A或B.线程A只能在{{1}时启动一个作业}设置为A,线程B只能在owner设置为B时启动作业。然后伪代码(假设线程A在这里):

owner

B线程只做反转while True: while True: # Loop until I'm the owner lock.acquire() if owner == A: break lock.release() # Now I'm the owner. And I still hold the lock. Start job. <Grab next job (or start job or finish job, whatever is required to remove it from contention)> owner = B lock.release() <Finish job if not already done. Go get next one> if owner语句的事情。显然,您可以对其进行参数化,以便两者实际上只运行相同的代码。

修改

这是工作版本,在对象中包含建议的逻辑:

owner =

答案 1 :(得分:1)

您可以排除错误的线程获取锁定的可能性,排除依赖time.sleep(...)的正确性并使用Queue同时缩短您的代码(两个队列用于双向通信):< / p>

import threading
import time
import random
from Queue import Queue

def work_hard(name, i):
  print('start %s - %s' % (name, i))
  time.sleep(random.uniform(0.2, 1))
  print('end %s - %s' % (name, i))

def worker(name, q_mine, q_his):
  for i in range(5):
    q_mine.get()
    work_hard(name, i)
    q_his.put(1)

qAB = Queue()
qBA = Queue()

t1 = threading.Thread(target=worker, args=('A', qAB, qBA))
t2 = threading.Thread(target=worker, args=('B', qBA, qAB))

t1.start()
qAB.put(1) # notice how you don't need time.sleep(...) even here
t2.start()

t1.join()
t2.join()

print('done')

它按照您的指定工作。或者,您可以使用threading.Conditionacquirereleasewaitnotify / notifyAll的组合,但这会更加微妙,特别是在哪个线程首先出现。