全局数据由一个线程写入并由多个线程读取

时间:2012-03-16 09:26:35

标签: c pthreads

有一个线程,只有一个线程将编写/修改数据结构。数据结构有一个字段,它是由写入线程打开的套接字描述符。另外两个线程将读取数据结构并且必须执行某些操作(例如轮询存储在结构中的套接字以获取输入数据)。

一个线程将读取由写入线程写入的一个全局结构,而另一个线程将读取第二个全局结构。实质上,两个读取线程不访问相同的全局结构。

我的问题是,如果写入线程对数据结构进行了更改,可能会在数据结构中添加更多记录或删除它们,那么进程是否会崩溃?是否存在任何可能发生运行时崩溃的实例的空间?

P.S。我正在使用在Linux环境中工作的POSIX线程

6 个答案:

答案 0 :(得分:1)

  

是否存在任何运行时崩溃可能的实例的空间   发生

简短的回答:很可能是的。您需要通过某种锁定来保护数据结构。想到了Pthread互斥体。

更长的答案:想想链接列表。考虑添加元素或删除元素。通常,您需要修改该列表的多个方面,例如,您需要修改2个指针。现在问问自己:如果出现会发生什么:

  • 编写器线程开始更改列表的某些方面(比如更新next指针),但在它被中断之前还没有完成
  • 读者线程试图从列表中读取

修改

正如Blagovest Buyukliev在评论中提到的那样,无锁数据结构通常会提供更多性能,因此您可以调查该路线。

答案 1 :(得分:1)

一般来说,答案是肯定的,你可以很容易地在整个地方崩溃。例如,如果一个线程读取链接列表的元素而另一个线程删除它,则会得到未定义的行为(读取:崩溃)。

最简单的方法是使用互斥锁,但是如果您需要使用某些高级技术并且可以访问compare and swap operation,则可以实现lock-free linked list,但是您应首先分析您的应用程序以查看提高速度值得你努力。

答案 2 :(得分:0)

虽然这取决于数据结构,但对于大多数数据结构,答案是:当然。

将可变长度向量视为最简单的示例。 writer线程通过递减元素计数然后进行一些清理来删除元素。读者线程遍历所有元素。现在假设读者线程以其循环到达最后一个元素,将索引与元素计数进行比较并即将访问该元素。发生上下文切换,编写器线程弹出最后一个元素,减少元素数量并清理元素。现在最后一个元素无效,上下文切换发生,读者访问无效元素。

答案 3 :(得分:0)

听起来您可能对pipes感兴趣。在写入/读取线程开始之前初始化两个管道并且将写入线程传递给管道的两个写入端并且将读取线程传递到它们各自管道的读取端。那么就不用担心同步问题了。

回答有关管道用于进程间通信的问题。我建议使用管道,因为你谈到使用套接字。用于网络间通信的套接字。这个post讨论了linux内核上管道和套接字之间的区别。对于本地机器通信,管道在速度和效率方面超过了套接字,因为它们比套接字具有更简单的实现。最后,你会得到没有线程的线程问题,因为内核会为你解决这个问题。

答案 4 :(得分:0)

嗯......你可以在没有锁定的情况下逃脱,但你需要小心:)数组/列表/任何容器中的每个元素都可以有一个状态枚举,也许:

enum state {EstFree,EstInUse,EstDeletePending}

要添加条目,编写器线程将迭代数组/列表,并使用状态为“EstFree”的第一个条目。如果没有,它会创建另一个元素并将其添加到最后。

要'删除''EstInUse'条目,请将其状态设置为'EdeletePending'。当使用此元素的线程注意到状态更改时,它知道停止轮询,因此它将状态设置为 'EstFree'并退出。

答案 5 :(得分:0)

你需要像其他人所说的那样避免比赛。如果您不想使用互斥锁来保护数据,那么现代C,C11将在语言级别上引入 atomic 操作。在直接更广泛支持之前,您可以使用compiler extension作为gcc和兼容性(clang,icc,opencc)或通过macros that emulate the new interface