我有一个场景,在我的程序中的某些点,线程需要更新几个共享数据结构。每个数据结构都可以与任何其他数据结构并行安全地更新,但每个数据结构一次只能由一个线程更新。我在代码中表达的简单,天真的方式是:
synchronized updateStructure1();
synchronized updateStructure2();
// ...
这似乎效率低下,因为如果多个线程正在尝试更新结构1,但没有线程尝试更新结构2,则它们都会阻塞等待保护结构1的锁,而结构2的锁则不会被取消。
有没有“标准”的方法来解决这个问题?换句话说,是否有一个标准的线程原语试图以循环方式更新所有结构,只有在所有锁被采用时才会阻塞,并在所有结构都被更新时返回?
这是一个与语言无关的问题,但如果它有帮助,我使用的语言是D.
答案 0 :(得分:2)
如果您的语言支持轻量级线程或Actors,您可以始终让更新线程生成一个新的线程来更改每个对象,其中每个线程只锁定,修改和解锁每个对象。然后让您的更新线程在返回之前加入其所有子线程。这会将问题归结为运行时的计划,并且可以以任何方式安排这些子线程以获得最佳性能。
你可以在线程较重的语言中执行此操作,但是spawn和join可能会有太多的开销(尽管线程池可能会减轻其中的一部分)。
答案 1 :(得分:1)
我不知道是否有标准方法可以做到这一点。但是,我会实现以下内容:
do
{
if (!updatedA && mutexA.tryLock())
{
scope(exit) mutexA.unlock();
updateA();
updatedA = true;
}
if (!updatedB && mutexB.tryLock())
{
scope(exit) mutexB.unlock();
updateB();
updatedB = true;
}
}
while (!(updatedA && updatedB));
一些聪明的元编程可能会减少重复次数,但我会将其作为练习留给你。
答案 2 :(得分:1)
对不起,如果我天真,但你不只是在对象上进行同步以使问题独立吗?
e.g。
public Object lock1 = new Object; // access to resource 1
public Object lock2 = new Object; // access to resource 2
updateStructure1() {
synchronized( lock1 ) {
...
}
}
updateStructure2() {
synchronized( lock2 ) {
...
}
}
答案 3 :(得分:0)
据我所知,没有一种标准的方法可以实现这一点,而且你必须弄清楚。
为了解释您的要求,您有一组数据结构,您需要对它们进行操作,但不能按任何特定顺序进行。如果所有其他对象都被阻止,您只想阻止等待数据结构。这是我将基于我的解决方案的伪代码:
work = unshared list of objects that need updating while work is not empty: found = false for each obj in work: try locking obj if successful: remove obj from work found = true obj.update() unlock obj if !found: // Everything is locked, so we have to wait obj = randomly pick an object from work remove obj from work lock obj obj.update() unlock obj
更新线程只有在发现需要使用的所有对象被锁定时才会阻止。然后它必须等待某事,所以它只需选择一个并锁定它。理想情况下,它会选择最早解锁的对象,但没有简单的方法可以解释。
此外,可以想象当updater在 try 循环中时对象可能变为空闲,因此更新程序会跳过它。但是,如果你所做的工作量足够大,相对于迭代循环的成本,虚假冲突应该是罕见的,并且只有在争用极高的情况下才会发生。
答案 4 :(得分:0)
我不知道这样做的任何“标准”方式,抱歉。所以下面只是一个ThreadGroup,由Swarm
类抽象,在工作列表中“hacks”直到完成所有操作,循环风格,并确保使用尽可能多的线程。如果没有工作清单,我不知道怎么做。
免责声明:我对D和并发编程都很陌生,因此代码非常业余。我认为这更像是一项有趣的运动。 (我也在处理一些并发性问题。)我也明白这不是你想要的。如果有人有任何指示,我很乐意听到他们!
import core.thread,
core.sync.mutex,
std.c.stdio,
std.stdio;
class Swarm{
ThreadGroup group;
Mutex mutex;
auto numThreads = 1;
void delegate ()[int] jobs;
this(void delegate()[int] aJobs, int aNumThreads){
jobs = aJobs;
numThreads = aNumThreads;
group = new ThreadGroup;
mutex = new Mutex();
}
void runBlocking(){
run();
group.joinAll();
}
void run(){
foreach(c;0..numThreads)
group.create( &swarmJobs );
}
void swarmJobs(){
void delegate () myJob;
do{
myJob = null;
synchronized(mutex){
if(jobs.length > 0)
foreach(i,job;jobs){
myJob = job;
jobs.remove(i);
break;
}
}
if(myJob)
myJob();
}while(myJob)
}
}
class Jobs{
void job1(){
foreach(c;0..1000){
foreach(j;0..2_000_000){}
writef("1");
fflush(core.stdc.stdio.stdout);
}
}
void job2(){
foreach(c;0..1000){
foreach(j;0..1_000_000){}
writef("2");
fflush(core.stdc.stdio.stdout);
}
}
}
void main(){
auto jobs = new Jobs();
void delegate ()[int] jobsList =
[1:&jobs.job1,2:&jobs.job2,3:&jobs.job1,4:&jobs.job2];
int numThreads = 2;
auto swarm = new Swarm(jobsList,numThreads);
swarm.runBlocking();
writefln("end");
}
答案 5 :(得分:0)
根据您的需要,没有标准解决方案,而是一类标准解决方案。