Seb*_*iot 21 c multithreading thread-safety nim-lang
我想在一个线程中分配内存,并安全地 "释放"指向另一个线程的指针,以便它可以读取该内存.
我正在使用一种翻译成C语言的高级语言.高级语言有线程(未指定的线程API,因为它是跨平台的 - 见下文)并支持标准的C多线程原语,如原子比较交换,但它没有真正记录(没有用法示例).这种高级语言的限制是:
现在,对于大型(不希望复制)或可变大小(我认为数组大小是类型的一部分)消息,这是不切实际的.我想发送这样的消息,这里是我想要实现它的大纲:
我需要知道如何确保这没有数据竞争.我的理解是我需要使用内存栅栏,但我不完全确定哪一个(ATOMIC_RELEASE,...)以及循环中的位置(或者我是否需要任何内容).
因为我的高级语言需要跨平台,所以我需要得到答案:
pthread_mutex_init
和pthread_mutex_lock
+pthread_mutex_unlock
InitializeCriticalSection
和EnterCriticalSection
+LeaveCriticalSection
如果它有帮助,我假设以下架构:
并使用以下编译器(您可以假设所有这些编译器的"最近"版本):
到目前为止,我只在Windows上构建,但是当应用程序完成后,我希望以最少的工作将其移植到其他平台.因此,我试图从一开始就确保跨平台兼容性.
这是我假设的工作流程:
真正的代码太大而无法发布.这里简化了(足以显示如何访问共享内存)使用互斥锁的伪代码(如消息队列):
static pointer p = null
static mutex m = ...
static thread_A_buffer = malloc(...)
Thread-A:
do:
// Send pointer to data
int index = findFreeIndex(thread_A_buffer)
// Assume different value (not 42) every time
thread_A_buffer[index] = 42
// Call some "memory fence" here (after writing, before sending)?
lock(m)
p = &(thread_A_buffer[index])
signal()
unlock(m)
// wait for processing
// in reality, would wait for a second signal...
pointer p_a = null
do:
// sleep
lock(m)
p_a = p
unlock(m)
while (p_a != null)
// Free data
thread_A_buffer[index] = 0
freeIndex(thread_A_buffer, index)
while true
Thread-B:
while true:
// wait for data
pointer p_b = null
while (p_b == null)
lock(m)
wait()
p_b = p
unlock(m)
// Call some "memory fence" here (after receiving, before reading)?
// process data
print *p_b
// say we are done
lock(m)
p = null
// in reality, would send a second signal...
unlock(m)
Run Code Online (Sandbox Code Playgroud)
这个解决方案有用吗?重新提出问题,Thread-B打印"42"?总是,在所有考虑过的平台和操作系统(pthreads和Windows CS)上?或者我是否需要添加其他线程原语,如内存栅栏?
我花了好几个小时看了许多相关的SO问题,并阅读了一些文章,但我仍然不完全确定.基于@Art评论,我可能不需要做任何事情.我相信这是基于POSIX标准4.12内存同步的声明:
[...]使用同步线程执行的函数,并使内存与其他线程同步.以下函数使内存与其他线程同步.
我的问题是这句话没有明确说明它们是指"所有被访问的内存",还是"只有在锁定和解锁之间访问的内存".我读过人们为这两种情况辩护的问题,甚至有些人暗示它是故意写的,是为了让编译器实现者在实现方面有更多的余地!
此外,这适用于pthreads,但我需要知道它如何适用于Windows线程.
我会根据标准文档中的引用/链接或其他一些高度可靠的来源选择任何答案,证明我不需要围栏或在上述平台配置下显示我需要的围栏,至少适用于Windows/Linux/MacOS案例.如果在这种情况下Windows线程的行为类似于pthreads,我也想要一个链接/引用.
以下是我读过的一些(最好的)相关问题/链接,但是存在冲突信息会让我怀疑我的理解.
我对C11:n1570.pdf的文档C++11
和类似措辞的回顾使我得出以下理解。
如果在线程之间执行某种形式的协作同步,则数据可以在线程之间安全地使用。如果有一个队列,在互斥锁内从队列中读取一个项目,并且如果在持有互斥锁的同时将项目添加到队列中,则第二个线程中可读的内存将是已写入队列中的内存。第一个线程。
\n\n这是因为编译器和底层 CPU 基础设施不允许组织通过排序的副作用。
\n\n从 n1570
\n\n\n\n\n如果 A 与 B 同步,则 A 线程间评估发生在 B 评估之前,A\n 在 B 之前按依赖顺序排序,或者,对于某些评估 X:
\n\n\xe2\x80\x94 A 与 X 同步,并且 X 在 B 之前排序,
\n\n\xe2\x80\x94 A 在 X 之前排序,并且 X 线程间在 B 之前发生,或者
\n\n\xe2\x80\x94 A 线程间发生在 X 之前,X 线程间发生在 B 之前
\n
所以要保证新线程中可见的内存一致,那么下面就可以保证结果了。
\n\n互锁写入会导致线程 A 上的所有前述操作在线程 B 看到读取之前进行排序并刷新缓存。
\n\n将数据写入队列以供“其他线程处理”后,第一个线程无法安全(解锁)修改或读取对象中的任何内存,直到它知道(通过某种机制)另一个线程不再访问数据。如果通过某种同步机制完成,它只会看到正确的结果。
\n\nC++ 和 C 标准都旨在形式化编译器和 CPU 的现有行为。因此,尽管在 pthread 和 C99 标准的使用方面存在不太正式的保证,但预计这些保证是一致的。
\n\n螺纹A
\n\nint index = findFreeIndex(thread_A_buffer)\n
Run Code Online (Sandbox Code Playgroud)\n\n这一行是有问题的,因为它没有显示任何同步原语。如果 findFreeIndex 的机制仅依赖于线程 A 写入的内存,那么这将起作用。如果线程 B 或任何其他线程修改了内存,则需要进一步锁定。
\n\nlock(m)\np = &(thread_A_buffer[index])\nsignal()\nunlock(m)\n
Run Code Online (Sandbox Code Playgroud)\n\n这涵盖了....
\n\n\n\n\n15 评估 A 在评估 B 之前按依赖性排序,如果
\n\n\xe2\x80\x94 A 对原子对象 M 执行释放操作,而在另一个线程中,B 对 M 执行消费操作并读取以 A 为首的释放序列中任何副作用写入的值,或者
\n\n\xe2\x80\x94 对于某些评估 X,A 在 X 之前是依存顺序的,并且 X 对 B 具有\n 依存关系。
\n
和
\n\n\n\n\n18 如果 A 在 B 之前排序,或者 A 线程间\n 在 B 之前发生,则评估 A 在评估 B 之前发生。
\n
同步之前的操作“发生在”同步之前,并且保证在同步之后在其他线程中可见。
\n\n锁定(获取)和解锁(释放)确保线程 A 中的信息有严格的顺序,该信息已完成且对 B 可见。
\n\nthread_A_buffer[index] = 42; // happens before \n
Run Code Online (Sandbox Code Playgroud)\n\n目前,内存 thread_A_buffer 在 A 上可见,但在 B 上读取它会导致未定义的行为。
\n\nlock(m); // acquire\n
Run Code Online (Sandbox Code Playgroud)\n\n尽管发布需要,但我看不到获取的任何结果。
\n\np = &thread_A_buffer[index];\nunlock(m);\n
Run Code Online (Sandbox Code Playgroud)\n\nA 的所有指令流现在对 B 都是可见的(由于它与 m 同步)。
\n\nthread_A_buffer[index] = 42; << This happens before and ...\np = &thread_A_buffer[index]; << carries a dependency into p\nunlock(m);\n
Run Code Online (Sandbox Code Playgroud)\n\nA 中的所有内容现在对 B 都是可见的,因为
\n\n\n\n\n如果 A 与 B 同步,A 的依赖顺序先于 B,或者对于某些评估 X,则评估 A 线程间在评估 B 之前发生
\n\n\xe2\x80\x94 A 与 X 同步,并且 X 在 B 之前排序,
\n\n\xe2\x80\x94 A 在 X 之前排序,并且 X 线程间在 B 之前发生,或者
\n\n\xe2\x80\x94 A 线程间发生在 X 之前,X 线程间发生在 B 之前。
\n
pointer p_a = null\ndo:\n // sleep\n lock(m)\n p_a = p\n unlock(m)\nwhile (p_a != null)\n
Run Code Online (Sandbox Code Playgroud)\n\n这段代码是完全安全的,读入 p_a 的值将与另一个线程排序,并且在线程 b 中同步写入后将不为空。同样,锁定/解锁会导致严格的排序,以确保读取的值将是写入的值。
\n\n线程 B 的所有交互都在锁内,因此也是完全安全的。
\n\n如果 A 在将对象提供给 B 后修改该对象,那么它将不起作用,除非有进一步的同步。
\n 归档时间: |
|
查看次数: |
597 次 |
最近记录: |