admin管理员组文章数量:1122832
Assume I'm using the following C# Minimal API endpoint:
private Calculator calcObj = new Calculator ();
app.MapGet("/calc", (int a, int b) =>
{
var additionResult = calcObj.Addition(a, b);
var multiplicationResult = calcObj.Multiplication(a, b);
return Results.Ok(new Message() { Text = "Result for addition: " + additionResult.ToString() + "Result for multiplication: " + multiplicationResult.ToString() });
});
This can be called by thousands of users at the same time.
So if the first user calls the API with a=1, b=2 and the second user calls it with a=3, b=4 and the next user with ... and so on...
How is every user getting their personal result without getting the calculation result "destroyed" by the next user's input?
I know the .NET runtime uses a thread pool to manage threads. When a request comes in, a thread from the pool is assigned to handle it. Then it should theoretically be no problem, right?
Or do I have to implement some locking mechanism?
Remark: the MyCalculator
is not using a database. It is built of structs and classes and C# data types only.
UPDATE: To make it more clear assume the calculator does the following (I know the members are not needed, just to dicuss the problem):
using System;
class Calculator
{
private double A;
private double B
public double Addition(a, b)
{
A=a;
B=b;
return A+B;
}
public double Multiplication(a, b)
{
A=a;
B=b;
return A*B;
}
}
Assume I'm using the following C# Minimal API endpoint:
private Calculator calcObj = new Calculator ();
app.MapGet("/calc", (int a, int b) =>
{
var additionResult = calcObj.Addition(a, b);
var multiplicationResult = calcObj.Multiplication(a, b);
return Results.Ok(new Message() { Text = "Result for addition: " + additionResult.ToString() + "Result for multiplication: " + multiplicationResult.ToString() });
});
This can be called by thousands of users at the same time.
So if the first user calls the API with a=1, b=2 and the second user calls it with a=3, b=4 and the next user with ... and so on...
How is every user getting their personal result without getting the calculation result "destroyed" by the next user's input?
I know the .NET runtime uses a thread pool to manage threads. When a request comes in, a thread from the pool is assigned to handle it. Then it should theoretically be no problem, right?
Or do I have to implement some locking mechanism?
Remark: the MyCalculator
is not using a database. It is built of structs and classes and C# data types only.
UPDATE: To make it more clear assume the calculator does the following (I know the members are not needed, just to dicuss the problem):
using System;
class Calculator
{
private double A;
private double B
public double Addition(a, b)
{
A=a;
B=b;
return A+B;
}
public double Multiplication(a, b)
{
A=a;
B=b;
return A*B;
}
}
Share
Improve this question
edited Nov 23, 2024 at 7:56
Guru Stron
140k11 gold badges162 silver badges205 bronze badges
asked Nov 22, 2024 at 15:00
user1911091user1911091
1,3612 gold badges16 silver badges37 bronze badges
2
|
2 Answers
Reset to default 2In the current code Minimal API handler will use the captured shared instance of the calculator (via the lambda closure mechanism). Since the calculator is not stateless (it mutates internal state) then you will have all sorts of concurrency issues here unless you will do proper synchronization. So you have following options:
- Perform proper synchronization
- Create instance of calculator per in the handler (i.e. )
app.MapGet("/calc", (int a, int b) => { var calc = new Calculator(); var additionResult = calc.Addition(a, b); // ... });
- Use DI and register the
Calculator
as Scoped/Transient (depending on the need,Scoped
will result in an instance shared per scope, which for handler is equal to the request) and resolve it in the handler :app.MapGet("/calc", (int a, int b, Calculator c) => ...
Concurrent users are only a problem if multiple users read and write[*] to the same memory. So:
- Is
calcObj
shared between multiple users? - Does
calcObj.Addition
orcalcObj.Multiplication
write to any shared memory? - Is there any form of synchronization that prevents concurrent access?
I'm not that familiar with ASP.NET or Minimal APIs, but I would guess that calcObj
is shared. A more typical approach is to use dependency injection for things like calcObj
. This lets you declare if the dependencies should be shared (i.e. singleton) or not (i.e. transient/scoped). This makes it much more explicit what your intentions are. Note the local variables, like additionResult
, live on the stack, and are never shared.
So does the add/multiply modify calcObj
or any other shared memory? Yes, A
and B
will be shared. But there is no good reason for A
and B
to exist, it would work just as well without these fields. It is generally a good idea to prefer "pure" methods, and immutable data structures. Both because these tend to be easier to reason about, and because they are thread safe by default.
As long as calcObj
keeps a shared state it would need to be protected. The easiest way would be to just use a lock inside both methods. If you use a lock you should try to hold it for as short period as possible, this both helps with contention and to avoid deadlocks. But this is mostly a concern when actually doing meaningful amount of computations.
But the recommended approach is to avoid shared mutable state whenever possible. I.e. make dependencies like calcObject
either transient or , preferably, immutable. If that is not possible you probably want to use a database. Databases will isolate any mutation and provide the necessary synchronization for you.
[*] Multiple concurrent writers are also a problem, but concurrent reads are fine.
本文标签: cHow does Minimal API handle concurrent requests from multiple usersStack Overflow
版权声明:本文标题:c# - How does Minimal API handle concurrent requests from multiple users? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736302935a1931709.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
MapGet
will have its owna
andb
, and also its own results, they are local - scoped to the function. However,calcObj
is created outside the function, so there is just one of them, with oneA
andB
. This will be shared across threads so it's totally feasible that user 2 can changeA
before user 1 uses it to calculate. The simplistic solution would be to create a new Calculator inside the function, but this pattern does not scale, the answers below suggest better patterns. – iakobski Commented Nov 23, 2024 at 14:05