我有大量的结构,如下所示:
typedef struct
{
int a;
int b;
int c;
etc...
}
data_type;
data_type data[100000];
我有一堆独立的线程,每个线程都希望对data []中的元素进行更改。我需要确保没有线程尝试同时访问相同的数据元素。确切地说:一个线程执行数据[475] .a = 3;和另一个执行数据的线程[475] .b = 7;同时不允许,但一个线程执行数据[475] .a = 3;而另一个线程执行数据[476] .a = 7;被允许。该程序高度速度至关重要。我的计划是为每个数据元素制作一个单独的关键部分,如下所示:
typedef struct
{
CRITICAL_SECTION critsec;
int a;
int b;
int c;
etc...
}
data_type;
从某种意义上说,我认为它应该全部工作,我应该没有真正的问题,但是在多线程编程方面没有多少经验我只是对有这么多关键部分感到有些不安。我想知道它们的绝对数量是否会造成某种低效率。我也想知道是否其他一些多线程技术可能会更快?我应该放松一下并继续计划A?
答案 0 :(得分:3)
有了这么多对象,他们的大多数关键部分都将被解锁,几乎没有争用。正如您已经知道的那样(其他注释),关键部分如果没有内存则不需要内核模式转换。这使得关键部分能够有效地应对这种情况。
唯一的另一个考虑因素是您是否需要对象内或其他数组中的关键部分。引用的位置是将关键部分放在对象内的一个很好的理由。当您进入临界区时,整个高速缓存行(例如16或32字节)将在内存中。通过一些填充,您可以确保每个对象都在缓存行上启动。因此,一旦输入其关键部分,该对象将(部分)在缓存中。
答案 1 :(得分:1)
您的计划值得尝试,但我认为您会发现Windows在创建许多关键部分时感到不快。每个CS包含一些内核句柄,并且您正在耗尽宝贵的内核空间。我认为,根据你的Windows版本,你将耗尽句柄内存和InitializeCriticalSection()或其他一些函数将开始失败。
您可能想要做的是拥有可供使用的CS池,并在结构中存储指向“使用中”CS的指针。但是这很快就会变得棘手,你将需要使用Atomic操作来设置/清除CS指针(以原子标记数组条目为'使用中')。可能还需要一些引用计数等...
变得复杂。
首先尝试自己的方式,看看会发生什么。我们曾经有类似的情况,我们不得不选择一个游泳池,但也许事情已经发生了变化。
答案 2 :(得分:1)
根据data_type结构中的数据成员类型(并且还取决于您要对这些成员执行的操作),您可能可以放弃使用单独的同步对象,而不是使用Interlocked函数。
在您的示例代码中,所有数据成员都是整数,并且所有操作都是赋值(并且可能是读取),因此您可以使用InterlockedExchange()以原子方式设置值,并使用InterlockedCompareExchange()以原子方式读取值。 / p>
如果需要使用非整数数据成员类型,或者需要执行更复杂的操作,或者需要一次协调对多个操作的原子访问(例如,读取数据[1]。 a然后写入数据[1] .b),那么你将不得不使用同步对象,例如CRITICAL_SECTION。
如果必须使用同步对象,我建议您考虑将数据集划分为子集,并为每个子集使用单个同步对象。例如,您可以考虑对数据数组中的每个1000个元素使用一个CRITICAL_SECTION。
答案 3 :(得分:0)
你也可以考虑MUTEX。 这是一个很好的方法。 每个客户端都可以使用互斥(互斥)保留资源。
这种情况比较常见,有些库也支持使用线程。 阅读boost::thread及其mutexes
用你的方法:
data_type data[100000];
我会害怕堆栈溢出,除非你在堆中分配它。
编辑:
升压:: MUTEX uses win32 Critical Sections
答案 4 :(得分:0)
正如其他人所指出的那样,是的,存在一个问题,它被称为过于细粒度的锁定......它的资源浪费,即使机会很小,你也会开始创建大量的支持原语和数据。得到一个偶尔的,比平时或任何其他,争论更长的时间。此外,您正在浪费资源,因为它并不是一个简单的数据结构,例如在VM impls中。
如果我没记错的话,从Win32上的那一点开始,你将有更高的机会获得SEH异常,或者只是更高的内存使用率。对它们进行分区和汇集可能是要走的路,但它是一个更复杂的实现。对其他事物(re:action)进行分区并期待一些短暂的争用是处理它的另一种方式。
无论如何,现在就是资源管理的问题。