“安全地”;借出;内存块到C中的另一个线程,假设没有;“并发访问”; 问题
我想在一个线程中分配内存,并安全地将指针“借给”另一个线程,以便它可以读取该内存 我使用的是一种高级语言,可以翻译成。高级语言有线程(未指定的线程API,因为它是跨平台的——见下文),并支持标准的C多线程原语,如原子比较交换,但它并没有真正的文档化(没有使用示例)。 这种高级语言的限制条件是:“安全地”;借出;内存块到C中的另一个线程,假设没有;“并发访问”; 问题,c,multithreading,thread-safety,nim-lang,C,Multithreading,Thread Safety,Nim Lang,我想在一个线程中分配内存,并安全地将指针“借给”另一个线程,以便它可以读取该内存 我使用的是一种高级语言,可以翻译成。高级语言有线程(未指定的线程API,因为它是跨平台的——见下文),并支持标准的C多线程原语,如原子比较交换,但它并没有真正的文档化(没有使用示例)。 这种高级语言的限制条件是: 每个线程执行一个事件处理无限循环 每个线程都有自己的本地堆,由一些自定义分配器管理 每个线程都有一个“输入”消息队列,该队列可以包含来自任意数量的其他线程的消息 消息传递队列包括: 对于固定类型的消息
- 每个线程执行一个事件处理无限循环
- 每个线程都有自己的本地堆,由一些自定义分配器管理
- 每个线程都有一个“输入”消息队列,该队列可以包含来自任意数量的其他线程的消息
- 消息传递队列包括:
- 对于固定类型的消息
- 使用复制
- 消息(请求或回复)可以内联存储“有效负载”(复制的、固定的总值大小限制),也可以在发送方堆中存储指向数据的指针
- 消息内容(发送方堆中的数据)归发送线程所有(分配和释放)
- 接收线程在处理完消息内容后向发送线程发送一个ack
- “发送”线程在收到(ack)之前,不得在发送消息内容后修改消息内容
- 在写操作完成之前,决不能对正在写入的内存进行并发读访问这应该由消息队列工作流程来保证。
可移植性考虑 因为我的高级语言需要跨平台,所以我需要解决以下问题:
- Linux、MacOS以及可选的Android和iOS
- 使用pthreads原语锁定消息队列:
和pthread\u mutex\u init
+pthread\u mutex\u lock
pthread\u mutex\u unlock
- 使用pthreads原语锁定消息队列:
- 窗户
- 使用Critical Section对象锁定消息队列:
,和初始化CriticalSection
+进入CriticalSection
离开CriticalSection
- 使用Critical Section对象锁定消息队列:
- 针对Windows/Linux/MacOS(?)的Intel/AMD PC体系结构
- iOS和Android的未知(ARM?)
- Windows上的MSVC
- Linux上的叮当声
- MacOS/iOS上的Xcode
- Android上的Android代码
尝试解决方案 以下是我假设的工作流程:
- 如果消息是“请求”,则可以对其进行处理,并将新消息缓冲为“答复”
- 如果消息是“回复”,则可以释放原始“请求”的消息内容(隐式请求“ack”)
- 如果消息是“回复”,并且它本身包含指向“回复内容”的指针(而不是“内联回复”),那么也必须发送“回复确认”
研究 我花了好几个小时研究了许多相关的SO问题,读了一些文章,但我仍然不能完全确定。基于@Art comment,我可能不需要做任何事情。我相信这是基于POSIX标准4.12内存同步中的以下声明: […]使用同步线程执行以及与其他线程同步内存的函数。以下函数与其他线程同步内存 我的问题是,这句话没有清楚地说明它们是指“所有访问的内存”,还是“仅在锁定和解锁之间访问的内存”。我读过有人对这两种情况都有争议,甚至有人暗示这是故意写得不准确的,以便给编译器实现者在实现中提供更多的余地 此外,这适用于pthreads,但我需要知道它如何适用于Windows线程 我将根据标准文档或其他高度可靠的来源中的引用/链接,选择证明我不需要围栏或显示我需要的围栏的任何答案,在上述平台配置下,至少在Windows/Linux/MacOS情况下。如果Windows线程的行为与本例中的pthreads类似,那么我也需要一个链接/引用 以下是我读到的一些(最好的)相关问题/链接,但冲突信息的存在使我怀疑我的理解
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)
int index = findFreeIndex(thread_A_buffer)
lock(m)
p = &(thread_A_buffer[index])
signal()
unlock(m)
thread_A_buffer[index] = 42; // happens before
lock(m); // acquire
p = &thread_A_buffer[index];
unlock(m);
thread_A_buffer[index] = 42; << This happens before and ...
p = &thread_A_buffer[index]; << carries a dependency into p
unlock(m);
pointer p_a = null
do:
// sleep
lock(m)
p_a = p
unlock(m)
while (p_a != null)