Multithreading 线程作业分配正确性

Multithreading 线程作业分配正确性,multithreading,thread-safety,Multithreading,Thread Safety,假设我有一堆工作线程,我想避免线程决定谁得到什么工作所花费的时间 假设每个线程都有一个与之关联的编号/ID。有一份工作清单。队列中的每个作业都有一个关联的ThreadID ThreadID 1 = Job Available 0 = Job Finished > 1 = Active ==> ThreadID = ID of thread working on the job 对于要在作业上工作的线程,它扫描列表并找到第一个Thre

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

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

ThreadID
1          = Job Available
0          = Job Finished
> 1        = Active ==> ThreadID = ID of thread working on the job
对于要在作业上工作的线程,它扫描列表并找到第一个ThreadID=1,然后尝试接受该作业

这样线程就消耗了作业。(很明显,他们会睡觉,需要被正确地叫醒,但暂时忽略所有这些)

问题是,两个线程可能同时尝试在同一个作业上工作,这是不好的

为了解决这个问题,每个线程只需将它的线程ID分配给线程ID,这将阻止其他线程处理作业,除非在写入线程ID之前读取一个线程

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的线程)


我相信这样的计划不需要锁,而且安全吗?这是否正确?

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

每个线程从队列中获取一个作业并对其进行处理。如果线程未能完全处理作业,则需要某种机制将作业返回队列


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

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

每个线程从队列中获取一个作业并对其进行处理。如果线程未能完全处理作业,则需要某种机制将作业返回队列

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

我认为您描述的“比较-写入-比较”方法是不够的。见本例:

  Thread 0          Thread 1

reads <empty>
                  reads <empty> 
writes 0
reads 0
runs task
                  writes 1
                  reads 1
                  runs task
线程0线程1
阅读
阅读
写入0
读数为0
运行任务
写1
阅读1
运行任务
这种交错将使两个线程执行相同的任务。我可能在你的描述中遗漏了一些东西,但如果我正确理解了你的算法,那么这应该与它的可靠性相矛盾。

我认为你所描述的“比较-写入-比较”方法是不够的。见本例:

  Thread 0          Thread 1

reads <empty>
                  reads <empty> 
writes 0
reads 0
runs task
                  writes 1
                  reads 1
                  runs task
线程0线程1
阅读
阅读
写入0
读数为0
运行任务
写1
阅读1
运行任务

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

这一切都取决于您用于实现此功能的编程语言的内存模型。但是,如果您使用,这应该是非常直接的。@DaoWen是的,如果CaS可用,但我相信我所说的比较-写入-比较也应该在没有锁定的情况下工作。这一切都取决于您用来实现这一点的编程语言的内存模型。但是,如果您使用,这应该是非常直接的。@DaoWen是的,如果CaS可用,但我相信我所说的关于比较-写入-比较的内容也应该在没有锁定的情况下工作。这是问题最后一部分的要点。通过使用compare-write-compare,应该不需要锁定,我说过,我想避免锁定。@user2541029,只有当一个值是一个现有值时,才有方法设置它,该值可以在不锁定的情况下执行您想要的操作。具体操作方式取决于您使用的语言。你在用哪种语言?这是问题最后一部分的重点。通过使用compare-write-compare,应该不需要锁定,我说过,我想避免锁定。@user2541029,只有当一个值是一个现有值时,才有方法设置它,该值可以在不锁定的情况下执行您想要的操作。具体操作方式取决于您使用的语言。你用哪种语言?是的,你是对的。我最初认为它将需要一个比较多次(在一个循环中),我认为最终仍然会有这样一个缺陷。我想最终需要一个CaS,这样才能让它工作。@user2541029-是的,你真的需要CaS。毕竟,这就是为什么他们将它添加到所有现代建筑中!你也可以做一些事情,比如让不同的线程写入不同的内存,但这远不如CaS。是的,你是对的。我最初认为它将需要一个比较多次(在一个循环中),我认为最终仍然会有这样一个缺陷。我想最终需要一个CaS,这样才能让它工作。@user2541029-是的,你真的需要CaS。毕竟,这就是为什么他们将它添加到所有现代建筑中!您也可以做一些类似的事情,比如让不同的线程写入不同的内存,但这远远低于CaS。