我应该关注这里的竞争条件吗?
Should I be concerned about a race condition here?
我在 javascript 中有一个 class,结构如下:
class TableManager {
/** an array containing Table objects **/
protected Tables = [];
protected getTable(tableId) {
// iterates over this.Tables, and searches for a table with a specific id: if found, it returns the table object, otherwise it returns null
}
protected async createTable(tableId) {
const Table = await fetchTable(tableId); /** performs an asynchronous operation, that creates a Table object by performing a select operation on the database **/
this.Tables.push(Table);
return Table;
}
protected async joinTable(user, tableId) {
const Table = this.getTable(tableId) ?? await this.createTable(tableId);
Table.addUser(user);
}
}
这个 class 背后的想法是它将通过套接字接收命令。例如,它可能收到 joinTable
命令,在这种情况下,它应该首先检查正在加入的 table 是否已经存在于内存中:如果存在,它将把用户添加到那个table,否则会创建table,存入内存,将用户添加到table。
我有点担心,如果在短时间内进行两次 joinTable()
调用,这可能会导致竞争条件,在这种情况下,将创建 tables两次,并作为两个单独的 table 实例存储在内存中。我对此感到害怕是对的吗?如果是,是否会在将 table 添加到 createTable
函数中的数组之前检查它是否存在,解决此竞争条件?
你的担心是对的。这个想法是交易,并确保在给定时间只有一个交易 运行。在 Nodejs 中,您可以使用 Mutex 来实现它。阅读更多:https://www.nodejsdesignpatterns.com/blog/node-js-race-conditions/.
I am a bit concerned, that this could result in a race condition, if
two joinTable() calls are made in a short amount of time, in which
case the tables will be created twice, and stored in memory as two
separate table instances. Am I right to be affraid about this?
只要您 await
每次调用(或正确链接),这应该不是问题。也就是说,只要操作是顺序的,就没有问题。如果你允许承诺同时解决(比如 Promise.all
)那么是的,就像现在一样,会有竞争条件。
If yes, would checking if the table exists before adding it to the array in
the createTable function, solve this race condition?
据我了解,不,它仍然会造成竞争条件。第一个函数调用将进行检查,发现 table 不存在并继续将查询发送到您的服务器以创建新条目。第二个函数调用也会进行检查,但由于它不等待上一个请求,因此检查可能在第一个请求完成之前发生(这是您的竞争条件)。这意味着可以发送另一个请求来创建另一个 table.
您可以做的是将您的条目存储为承诺。我会为此使用 Map
:
protected Tables = new Map();
protected getTable(tableId) {
let Table = this.Tables.get(tableId);
if(!Table){
Table = fetchTable(tableId);
this.Tables.set(tableId, Table);
}
return Table;
}
这样,joinTable
可以改为执行 getTable
,这也会创建 Table
(如果不存在)。如果 Table
正在创建,它将接受承诺并且不会以这种方式重复。
最终,无论是否在服务器上创建任何实体,都需要在服务器上...在服务器上进行管理。否则,您可能会冒多个客户端(甚至客户端重新启动)创建这些副本的风险。
我在 javascript 中有一个 class,结构如下:
class TableManager {
/** an array containing Table objects **/
protected Tables = [];
protected getTable(tableId) {
// iterates over this.Tables, and searches for a table with a specific id: if found, it returns the table object, otherwise it returns null
}
protected async createTable(tableId) {
const Table = await fetchTable(tableId); /** performs an asynchronous operation, that creates a Table object by performing a select operation on the database **/
this.Tables.push(Table);
return Table;
}
protected async joinTable(user, tableId) {
const Table = this.getTable(tableId) ?? await this.createTable(tableId);
Table.addUser(user);
}
}
这个 class 背后的想法是它将通过套接字接收命令。例如,它可能收到 joinTable
命令,在这种情况下,它应该首先检查正在加入的 table 是否已经存在于内存中:如果存在,它将把用户添加到那个table,否则会创建table,存入内存,将用户添加到table。
我有点担心,如果在短时间内进行两次 joinTable()
调用,这可能会导致竞争条件,在这种情况下,将创建 tables两次,并作为两个单独的 table 实例存储在内存中。我对此感到害怕是对的吗?如果是,是否会在将 table 添加到 createTable
函数中的数组之前检查它是否存在,解决此竞争条件?
你的担心是对的。这个想法是交易,并确保在给定时间只有一个交易 运行。在 Nodejs 中,您可以使用 Mutex 来实现它。阅读更多:https://www.nodejsdesignpatterns.com/blog/node-js-race-conditions/.
I am a bit concerned, that this could result in a race condition, if two joinTable() calls are made in a short amount of time, in which case the tables will be created twice, and stored in memory as two separate table instances. Am I right to be affraid about this?
只要您 await
每次调用(或正确链接),这应该不是问题。也就是说,只要操作是顺序的,就没有问题。如果你允许承诺同时解决(比如 Promise.all
)那么是的,就像现在一样,会有竞争条件。
If yes, would checking if the table exists before adding it to the array in the createTable function, solve this race condition?
据我了解,不,它仍然会造成竞争条件。第一个函数调用将进行检查,发现 table 不存在并继续将查询发送到您的服务器以创建新条目。第二个函数调用也会进行检查,但由于它不等待上一个请求,因此检查可能在第一个请求完成之前发生(这是您的竞争条件)。这意味着可以发送另一个请求来创建另一个 table.
您可以做的是将您的条目存储为承诺。我会为此使用 Map
:
protected Tables = new Map();
protected getTable(tableId) {
let Table = this.Tables.get(tableId);
if(!Table){
Table = fetchTable(tableId);
this.Tables.set(tableId, Table);
}
return Table;
}
这样,joinTable
可以改为执行 getTable
,这也会创建 Table
(如果不存在)。如果 Table
正在创建,它将接受承诺并且不会以这种方式重复。
最终,无论是否在服务器上创建任何实体,都需要在服务器上...在服务器上进行管理。否则,您可能会冒多个客户端(甚至客户端重新启动)创建这些副本的风险。