上下文:我试图在TypeScript中实现一个基本的套接字池。我当前的实现只是一个套接字的列表,其中有一个“可用/占用”枚举(不可否认,它可能是一个布尔值),它允许我有一个互斥机制,以确保每个套接字一次只发送/接收一个消息。
我所理解的是:我知道Node.js处理“并行”操作的方式是“单线程异步”。
我推断:对我来说,这意味着一次只有一个“控制指针”/“代码读头”/“控制流位置”,因为只有一个线程。在我看来,当“等待”被调用时,readhead只会跳转到代码中的其他地方,而我“等待”的承诺还无法解决。但我不知道情况是否真的如此。
我想知道的是:“单线程异步”是否确保除了调用“等待”之外,在任何时间都不会跳转控制流位置?或者是否有一些潜在的调度程序确实会在任意时刻导致任务之间的跳转,比如正常的多线程处理?
我的问题:所有这些都要问,我是否需要一个纯互斥/比较和交换机制来确保我的互斥类可用/占用字段被适当设置?
考虑以下代码:
export enum TaskSocketStatus
{
AVAILABLE, //Alive and available
OCCUPIED, //Alive and running a task
}
export interface TaskSocket
{
status:TaskSocketStatus;
socket:CustomSocket;
}
export class Server //A gateway that acts like a client manager for an app needing to connect to another secure server
{
private sockets:TaskSocket[];
[...]
private async Borrow_Socket():Promise<TaskSocket|null>
{
for (const socket of this.sockets)
{
if (!socket.socket.Is_Connected())
{
await this.Socket_Close(socket);
continue;
}
if (socket.status === TaskSocketStatus.AVAILABLE)
{
//This line is where things could go wrong if the control flow jumped to another task;
//ie, where I'd need a mutex or compare-and-swap before setting the status
socket.status = TaskSocketStatus.OCCUPIED;
return (socket);
}
}
if (this.sockets.length < this.max_sockets)
{
const maybe_socket = await this.Socket_Create();
if (maybe_socket.isError())
{
return null;
}
//Probably here as well
maybe_socket.value.status = TaskSocketStatus.OCCUPIED;
return maybe_socket.value;
}
return null;
}
[...]
}
我希望避免的问题是,由于竞争条件,两个不同的"SendMessage“任务借用相同的套接字。也许这是不必要的担心,但我想确保,因为这是一个潜在的问题,我真的不想面对当服务器已经在生产……
谢谢你的帮助!
发布于 2020-07-29 15:29:22
因此,当调用await
时,另一个操作的控制流不是。当运行的Javascript片段返回到event循环时,event循环可以为下一个等待事件提供服务。已解析的承诺也可以通过事件循环工作(一个特殊的队列,但仍然在事件循环中)。
所以,当你点击await
时,它不会立即跳转到其他地方。它将暂停该函数的进一步执行,然后导致该函数立即返回一个承诺,并且控制将继续,并将一个承诺返回给该函数的调用方。调用方的代码在收到承诺后继续执行。只有当调用方或调用方的调用方或调用方的调用方(取决于调用堆栈的深度)从启动整个执行链的事件循环返回到事件循环时,事件循环才有机会为下一个事件提供服务并启动新的执行链。
稍后,当连接到原始await
的底层异步操作完成时,它将将一个事件插入到事件队列中。当其他Javascript执行将控制权返回到事件循环,而该事件到达事件队列的开始时,它将被执行,并将解析await
等待的承诺。只有这样,在await
有机会运行之后,函数中的代码才会运行。当包含该async
函数的await
最终完成它的内部执行时,那么最初在第一个await
被击中时从async
函数返回的承诺将被解析,并将通知调用者它得到的承诺已经解决(假设它在这个承诺上使用了await
或.then()
)。
所以,从一个地方跳到另一个地方是不可能的。Javascript执行的当前线程将控制返回到事件循环(通过返回和展开其调用堆栈),然后事件循环可以为下一个等待事件服务,并启动一个新的执行链。只有当执行链完成并返回时,事件循环才能得到下一个事件并启动另一个执行链。以这种方式,只有一个调用堆栈帧一次运行。
在您的代码中,我不太理解您关心的内容。在Javascript中没有先发制人的转换。如果您的函数执行了await
,那么它的执行将在此时暂停,其他代码可以在承诺得到解决之前运行,并在await
之后继续执行。但是,没有先发制人的切换可以改变上下文并在这个线程中运行其他代码,而不需要调用一些异步操作,然后继续进行完整的回调或await
之后。
因此,从纯Javascript的角度来看,不涉及异步操作的纯本地Javascript语句之间并不存在任何问题。这些代码保证是连续的、不间断的(我们假设您的代码中没有使用共享内存和工作线程的代码--在您发布的代码中没有任何迹象)。
我想知道的是:“单线程异步”是否确保除了调用“等待”之外,在任何时间都不会跳转控制流位置?
它确保控制流位置在任何时候都不会跳转,除非您返回事件循环(解除调用堆栈)。它不发生在await
。await
可能导致函数返回,并可能导致调用方返回到事件循环,同时等待返回的承诺解析,但重要的是要了解,只有当堆栈展开并将控制返回到事件循环时,才会发生控制流更改,以便可以从事件队列中提取下一个事件并进行处理。
还是有一些潜在的调度程序确实会在任意时刻导致任务之间的跳转,比如正常的多线程处理?
假设我们不是在讨论Worker,nodejs中没有先发制人的Javascript线程切换。只有当Javascript的当前线程返回回事件循环时,对另一段Javascript的执行才会发生更改。
我的问题:所有这些都要问,我是否需要一个纯互斥/比较和交换机制来确保我的互斥类可用/占用字段被适当设置?
不,你不需要互斥。在测试和set之间不返回事件循环,因此它们不会被任何其他代码中断。
https://stackoverflow.com/questions/63163441
复制相似问题