【问题标题】:Thread job assignment correctness线程作业分配正确性
【发布时间】:2013-07-02 03:10:09
【问题描述】:

假设我有一堆工作线程,我想避免线程花费时间来确定谁得到什么工作。

假设每个线程都有一个与之关联的数字/ID。有一份工作清单。队列中的每个作业都有一个与之关联的 ThreadID。

ThreadID
1          = Job Available
0          = Job Finished
> 1        = Active ==> ThreadID = ID of thread working on the job

要让一个线程处理一项工作,它会扫描列表并找到第一个 ThreadID = 1 并尝试接受该工作。

这样线程会消耗作业。 (显然他们会睡觉,需要被正确唤醒,但现在忽略所有这些)

问题是,两个线程可能同时尝试处理同一个作业,这很糟糕。

要解决这个问题,每个线程只需将其线程 ID 分配给线程 ID,这将阻止其他线程处理该作业除非在写入线程 ID 之前读取一个线程。 p>

ThreadID      Thread 11         Thread 12            ....
1             ThreadID == 1?                         Job available
                                ThreadID == 1?       Job available
11            ThreadID = 11                          Try to take job
12                              ThreadID == 12       Try to take job
              ThreadID == 11?                        Job was taken by another thread
                                ThreadID == 12?      (If no other competing threads then thread 12 got the job)

不确定该表是否有意义,但它显示了两个线程在竞争该作业。他们都认为他们有这份工作,但无论哪个线程实际上在 ThreadId 中有他们的编号,都得到了这份工作(它将是最后一个写入 ThreadID 的线程)。

我相信这样的方案不需要锁并且安全吗?这是正确的吗?

【问题讨论】:

  • 这一切都取决于你用来实现它的编程语言的内存模型。但是,如果您使用 compare and swap,这应该非常简单。
  • @DaoWen 是的,如果 CaS 可用,但我相信我所说的比较写入比较应该也可以在没有锁定的情况下工作。

标签: multithreading thread-safety


【解决方案1】:

通常,当您有一组作业和多个线程来处理作业时,您将通过锁定或更复杂的非阻塞机制将作业放入线程安全队列中,例如 .NET 中的 ConcurrentQueue<T> 4.

每个线程从队列中获取一个作业并处理它。如果线程无法完全处理它,您需要一些机制来返回队列。

但是,如果您想继续使用将作业标记为由线程处理的方法,这样做很简单,但您需要使用锁定来确保一次只有一个线程修改作业。

【讨论】:

  • 这是问题最后一部分的重点。通过使用 compare write compare,应该不需要锁定,我说过,我想避免。
  • @user2541029,有一些方法可以设置一个值,前提是它是一个现有的值,它可以在没有锁定的情况下做你想做的事情。具体如何取决于您使用的语言。您使用哪种语言?
【解决方案2】:

我认为您所描述的“比较-写入-比较”方法是不够的。看这个案例:

  Thread 0          Thread 1

reads <empty>
                  reads <empty> 
writes 0
reads 0
runs task
                  writes 1
                  reads 1
                  runs task

这种交错将使两个线程执行相同的任务。我可能遗漏了您的描述中的某些内容,但如果我正确理解了您的算法,那么这应该与其合理性相矛盾。

【讨论】:

  • 是的,你是对的。我最初认为它需要一个人进行多次比较(在一个循环中),我认为最终仍然会有这样的缺陷。我想最终需要一个 CaS 才能让它工作。
  • @user2541029 - 是的,您确实需要 CaS。毕竟,这就是他们将它添加到所有现代架构中的原因!您也可以执行类似Peterson's algorithm 之类的操作,让不同的线程写入不同的内存,但这远不如 CaS 高效。
猜你喜欢
  • 1970-01-01
  • 2022-01-24
  • 2018-12-29
  • 1970-01-01
  • 2020-07-23
  • 1970-01-01
  • 1970-01-01
  • 2011-03-05
  • 2017-05-15
相关资源
最近更新 更多