我有一个我个人实现的数据结构,现在需要跨多个线程使用。
typedef struct
{
void** array_of_elements;
size_t size;
} myStruct;
为简单起见,假设我的数据结构具有以下函数:
// Gets a data element from the structure.
void* get(myStruct *x);
// Prints out all the data elements.
void print(myStruct *x);
// Adds an element into the structure.
void add(myStruct *x, void *to_be_added);
当另一个线程调用get
时调用print
没有任何问题,因为它们都是访问器。但是,当前调用add
时,get
和print
无法正常工作。反之亦然,如果当前正在进行get
和print
,则add
不起作用。
所以我myStruct
更改为如下所示:
typedef struct
{
void** array_of_elements;
size_t size;
// True when a mutator is editing this struct.
bool mutating;
// The number of threads currently accessing this struct.
int accessors;
} myStruct;
现在我的函数如下所示:
void* get(myStruct *x)
{
// Wait for mutating to end.
while (x->mutating);
// Indicate that another accessor is now using this struct.
x->accessors++;
// get algorithm goes here
// Declare we are finished reading.
x->accessors--;
return ...
}
// Same as above...
void print(myStruct *x)
...
void add(myStruct *x)
{
// Wait for any accessors or mutators to finish.
while (x->mutating || x->accessors > 0);
x->mutating = true;
// add algorithm here
x->mutating = false;
}
但是,我认为这种方法有很多问题,我找不到解决它们的方法:
- 我的一位同学告诉我,使用 while 循环会极大地减慢线程的速度。
- 它没有队列的感觉。开始等待
myStruct
完成使用的第一种方法不一定是下一个方法。 - 即使我有一个队列数据结构,下一个线程,该数据结构也需要同步,这本身就是一个需要同步数据结构来同步自身的无限循环。
- 我认为有可能在同一个纳秒内,一个线程将
accessors
计数器从0
更改为1
(这意味着他们想要开始读取),突变线程可能会看到它的值是0
并开始变异。然后,突变器线程和访问器线程将同时运行。 - 我很确定这种逻辑会导致堵塞(线程无限等待)。
- 我不知道如何让某些线程在需要执行此任务时休眠和唤醒,除了让它卡在
while
循环中。
你有正确的想法,只是错误的方法。 我不确定你在什么操作系统上编程,但你想看看mutex
或semaphore
的概念来做你想做的事情。
在符合 POSIX 标准的 Linux/Unix 上,您可以查看 pthreads:
http://www.cs.wm.edu/wmpthreads.html
在 Windows 上,您可以查看Critical Sections
,寻找接近mutex
概念的内容:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms682530(v=vs.85).aspx
或者WaitForMultipleObjects
接近semaphore
的东西:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(v=vs.85).aspx
而且,是的,使用while
循环是一个坏主意。 在本例中,您使用的是所谓的繁忙循环。 更多阅读在这里:
什么是繁忙循环?
使用mutex
或semaphore
,不需要 while 循环。 祝你好运!