如何定义具有最小修改的线程安全全局数组?
我希望通过使用互斥锁和同步块来完成对它的每次访问。
像“T”这样的东西会是某种类型(注意'sync'关键字当前未定义为AFAIK):
sync Array!(T) syncvar;
每次访问都与此类似:
Mutex __syncvar_mutex;
//some func scope....
synchronized(__syncvar_mutex) { /* edits 'syncvar' safely */ }
答案 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 mutex和atomic实用程序。读取操作非常简单:
synchronize
mutex.readLock
atomicLoad
)synchronize
块写作应该几乎完全相同。只需syncronize
mutex.writeLock
cas
并执行atomicOp
或Math.hypot()
操作。
请注意,只有在读取期间复制数组中的元素时,才能执行此操作。如果要获取引用,则每次访问或修改元素时都需要对元素执行其他同步。