高效的独立同步块?

时间:2010-07-12 17:26:55

标签: performance multithreading language-agnostic synchronization d

我有一个场景,在我的程序中的某些点,线程需要更新几个共享数据结构。每个数据结构都可以与任何其他数据结构并行安全地更新,但每个数据结构一次只能由一个线程更新。我在代码中表达的简单,天真的方式是:

synchronized updateStructure1();
synchronized updateStructure2();
// ...

这似乎效率低下,因为如果多个线程正在尝试更新结构1,但没有线程尝试更新结构2,则它们都会阻塞等待保护结构1的锁,而结构2的锁则不会被取消。

有没有“标准”的方法来解决这个问题?换句话说,是否有一个标准的线程原语试图以循环方式更新所有结构,只有在所有锁被采用时才会阻塞,并在所有结构都被更新时返回?

这是一个与语言无关的问题,但如果它有帮助,我使用的语言是D.

6 个答案:

答案 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)

根据您的需要,没有标准解决方案,而是一类标准解决方案。

http://en.wikipedia.org/wiki/Scheduling_algorithm