c# - C#.NET 使用异步方法防止用户同时调用 API 两次
问题描述
using System;
using System.Threading;
namespace Examples.AdvancedProgramming.AsynchronousOperations
{
public class AsyncMain
{
public static void Main()
{
// The asynchronous method puts the thread id here.
int threadId;
// Create an instance of the test class.
AsyncDemo ad = new AsyncDemo();
// Create the delegate.
AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);
// Initiate the asychronous call.
IAsyncResult result = caller.BeginInvoke(3000,
out threadId, null, null);
Thread.Sleep(0);
Console.WriteLine("Main thread {0} does some work.",
Thread.CurrentThread.ManagedThreadId);
// Call EndInvoke to wait for the asynchronous call to complete,
// and to retrieve the results.
string returnValue = caller.EndInvoke(out threadId, result);
Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
threadId, returnValue);
}
}
}
EndInvoke
可能会阻塞调用线程,因为它在异步调用完成之前不会返回。
我将EndInvoke
在一个 API 中实现,以防止用户调用 API 两次。我是否使用正确的方法来防止同一用户同时调用 API 两次?
解决方案
如果我可以假设AsyncDemo
在此MSDN 文章中实现了:
public class AsyncDemo
{
// The method to be executed asynchronously.
public string TestMethod(int callDuration, out int threadId)
{
Console.WriteLine("Test method begins.");
Thread.Sleep(callDuration);
threadId = Thread.CurrentThread.ManagedThreadId;
return string.Format("My call time was {0}.", callDuration.ToString());
}
}
并且相关的AsyncMethodCaller
委托被定义为:
public delegate string AsyncMethodCaller(int callDuration, out int threadId);
那么您有两个选项,具体取决于 .NET 版本:
.NET 框架
这里我们可以使用Task.FromAsync
( 1 ) 将 APM 模型转换为 TPL。
我必须定义两个辅助方法 ( Begin
, End
) 来将额外的参数传递给TestMethod
.
该End
方法返回一个元组(string, int)
以避免out
参数。(一)
public static async Task Main()
{
var ad = new AsyncDemo();
AsyncMethodCaller caller = ad.TestMethod;
IAsyncResult Begin(AsyncCallback cb, object state = null)
=> caller.BeginInvoke(3000, out _, cb, state);
(string, int) End(IAsyncResult iar)
{
var result = caller.EndInvoke(out var threadId, iar);
return (result, threadId);
};
var task1 = Task.Factory.FromAsync(Begin, End, null);
var task2 = Task.Factory.FromAsync(Begin, End, null);
await Task.WhenAll(task1, task2);
var (result1, threadId1) = await task1;
var (result2, threadId2) = await task1;
Console.WriteLine($"{threadId1}: {result1}");
Console.WriteLine($"{threadId2}: {result2}");
}
输出将是:
Test method begins.
Test method begins.
3: My call time was 3000.
3: My call time was 3000.
.NET 核心
在这里我们不能使用BeginInvoke
,因为它会抛出一个PlatformNotSupportedException
.
这里的解决方法是使用Task.Run
.
static async Task Main(string[] args)
{
int threadId1 = 0, threadId2 = 0;
var ad = new AsyncDemo();
AsyncMethodCaller caller = ad.TestMethod;
var task1 = Task.Run(() => caller(3000, out threadId1));
var task2 = Task.Run(() => caller(3000, out threadId2));
await Task.WhenAll(task1, task2);
Console.WriteLine($"{threadId1}: {await task1}");
Console.WriteLine($"{threadId2}: {await task2}");
}
输出将是:
Test method begins.
Test method begins.
4: My call time was 3000.
5: My call time was 3000.
推荐阅读
- vba - VBA 在一个范围内获得多个值
- asp.net-core - Visual Studio 2017:无法加载 SDK 解析器类型“DotNetMSBuildSdkResolver”
- asp.net-web-api - 如何通过使用 axios 访问 Web API GET 方法?
- http - Mule 4 在 Dataweave 中设置 HTTP 状态
- sql-server-2008 - 您可以使用该过程中保存的登录名运行 SQL Server 存储过程吗
- testing - 在 Phoenix 中测试返回 nil 的虚拟场
- mysql - 为什么我的字段没有使用表中的实时数据进行更新?
- sql - 从 SQL 查询生成 XML 文件
- python - Python 列保留原始更新的“NA”;永远不会用浮动更新
- javafx - 使用 javafx 创建一个 AutoCompleteTextField