如何定义线程安全数组?

时间:2015-04-22 19:04:18

标签: arrays multithreading synchronization d

如何定义具有最小修改的线程安全全局数组?

我希望通过使用互斥锁和同步块来完成对它的每次访问。

像“T”这样的东西会是某种类型(注意'sync'关键字当前未定义为AFAIK):

sync Array!(T) syncvar;

每次访问都与此类似:

Mutex __syncvar_mutex;

    //some func scope....
    synchronized(__syncvar_mutex) { /* edits 'syncvar' safely */ }

4 个答案:

答案 0 :(得分:2)

我天真的尝试是做这样的事情:

import std.typecons : Proxy:

synchronized class Array(T)
{
    static import std.array;
    private std.array.Array!T data;
    mixin Proxy!data;
}

可悲的是,由于https://issues.dlang.org/show_bug.cgi?id=14509

,它无效

不能说我很惊讶,因为在现代D中,通过隐藏互斥体进行多线程的自动处理是非常单一的,并且同步类的概念主要是D1次的残留。

当然,您可以手动实现相同的解决方案,方法是使用所有必需的方法定义自己的SharedArray类,并在调用内部私有Array方法之前在方法内添加锁。但我认为你想要更开箱即用的东西。

不能在这里和现在发明任何更好的东西(会考虑更多)但值得注意的是,一般来说,D鼓励创建专为处理共享访问而设计的数据结构,而不仅仅是保护正常的数据结构用互斥量。当然,大多数鼓励的方法是不使用消息传递来共享数据。

如果有更好的事情发生,我会更新答案。

答案 1 :(得分:0)

您可以将数组包装在 struct 中,该线程在线程获取令牌时锁定对该数组的访问,直到它释放它为止。

包装/储物柜:

  • acquire():由一个线程在循环中调用。当它返回一个指针时,线程知道当方法返回非空值时它有令牌。
  • release():在处理先前已获取访问权限的数据后由线程调用。

shared struct Locker(T)
{
    private:
        T t;
        size_t token;   
    public:  
        shared(T) * acquire() 
        {
            if (token) return null;
            else
            {
                import core.atomic;
                atomicOp!"+="(token, 1);
                return &t;
            }
        }
        void release()
        {
            import core.atomic;
            atomicOp!"-="(token, 1);
        }
}

并快速测试:

alias LockedIntArray = Locker!(size_t[]);
shared LockedIntArray intArr;

void arrayTask(size_t cnt)
{
    import core.thread, std.random;

    // ensure the desynchronization of this job.
    Thread.sleep( dur!"msecs"(uniform(4, 20)));

    shared(size_t[])* arr = null;
    // wait for the token
    while(arr == null) {arr = intArr.acquire;}

    *arr ~= cnt;    
    import std.stdio;
    writeln(*arr);

    // release the token for the waiting threads
    intArr.release;
}

void main(string[] args)
{
    import std.parallelism;
    foreach(immutable i; 0..16)
    {
       auto job = task(&arrayTask, i);
       job.executeInNewThread(); 
    } 
}

缺点是数组上的每个操作块都必须用 acquire / release 对包围。

答案 2 :(得分:0)

在数组周围创建一个包装器会很容易使它成为线程安全的。但是,创建一个不是并发瓶颈的线程安全数组非常困难。

最让人想到的是Java的CopyOnWriteArrayList类,但即使这样也不理想......

答案 3 :(得分:-1)

你有正确的想法。作为一个数组,您需要能够编辑和检索信息。我建议您查看Phobos提供的read-write mutexatomic实用程序。读取操作非常简单:

  1. synchronize mutex.readLock
  2. 加载(使用atomicLoad
  3. 将项目从synchronize
  4. 中复制出来
  5. 返回复制的项目
  6. 写作应该几乎完全相同。只需syncronize mutex.writeLock cas并执行atomicOpMath.hypot()操作。

    请注意,只有在读取期间复制数组中的元素时,才能执行此操作。如果要获取引用,则每次访问或修改元素时都需要对元素执行其他同步。