- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
首先,请原谅我的英语。我将在 Task.WhenAny 之后的附加代码中进行简要说明,我期望的是五个任务中至少有三个将被取消,但都圆满结束。当任务被取消时,SemaphoreSlim.WaitAsync 不会抛出 OperationCanceledException。
class Program
{
private static CancellationTokenSource methodRequests = new CancellationTokenSource();
private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
static void Main(string[] args)
{
int[] delays = new int[] { 5000, 5010, 5020, 5030, 5040 };
IEnumerable<Task> tasks = from delay in delays select MethodAsync(delay, new CancellationTokenSource().Token);
Task.WhenAny(tasks).Wait();
methodRequests.Cancel();
Console.ReadKey();
}
static async Task MethodAsync(int milliseconds, CancellationToken cancellationToken)
{
var methodRequest = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, methodRequests.Token);
try
{
await semaphore.WaitAsync(methodRequest.Token);
Thread.Sleep(milliseconds);
Console.WriteLine($"Task finished {milliseconds}");
}
catch (OperationCanceledException)
{
Console.WriteLine($"Task canceled {milliseconds}");
}
finally
{
semaphore.Release();
}
}
}
我做错了什么?
谢谢。
最佳答案
您的代码中的问题是 MethodAsync()
方法在 Thread.Sleep()
方法完成之前永远不会返回。这意味着在前一个任务完成之前,每个任务甚至都不会开始。这是您的代码版本,可以更清楚地说明这一点:
private static CancellationTokenSource methodRequests = new CancellationTokenSource();
private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
static void Main(string[] args)
{
int[] delays = new int[] { 5000, 5010, 5020, 5030, 5040 };
IEnumerable<Task> tasks = from delay in delays select MethodAsync(delay, new CancellationTokenSource().Token);
Task.WhenAny(tasks).Wait();
methodRequests.Cancel();
ReadKey();
}
static async Task MethodAsync(int milliseconds, CancellationToken cancellationToken)
{
var methodRequest = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, methodRequests.Token);
try
{
WriteLine($"waiting semaphore (will wait {milliseconds} ms)");
await semaphore.WaitAsync(methodRequest.Token);
WriteLine($"waiting {milliseconds} ms");
Thread.Sleep(milliseconds);
WriteLine($"Task finished {milliseconds}");
}
catch (OperationCanceledException)
{
WriteLine($"Task canceled {milliseconds}");
}
finally
{
semaphore.Release();
}
}
输出是:
waiting semaphore (will wait 5000 ms)waiting 5000 msTask finished 5000waiting semaphore (will wait 5010 ms)waiting 5010 msTask finished 5010waiting semaphore (will wait 5020 ms)waiting 5020 msTask finished 5020waiting semaphore (will wait 5030 ms)waiting 5030 msTask finished 5030waiting semaphore (will wait 5040 ms)waiting 5040 msTask finished 5040
如您所见,在上一个任务完成之前,您甚至都不会看到“正在等待信号量...” 消息。这是因为在 MethodAsync()
方法返回当前元素的值之前,您的 LINQ select
无法继续处理序列中的下一个元素,并且直到Thread.Sleep()
完成。
您可能认为 await semaphore.WaitAsync()
应该让步给调用者,允许返回 Task
和 select 的枚举
继续。但是,只有在信号量不可用时才会发生这种情况。它在每次调用时都可用,因为每次调用仅在前一个调用完成后发生,因为在进行前一个调用时,信号量可用。由于在调用 WaitAsync()
时信号量可用,因此 await
同步完成。 IE。代码直接进入 Thread.Sleep()
而不是让步给调用者。
最终效果是 WhenAny()
的调用甚至不会发生,直到 all Thread.Sleep()
调用(当然,所有 semaphore.WaitAsync()
调用也已完成)。
大概这是在某些真实场景中出现的,您发布的代码仅用于演示目的。因此,很难准确地说出您应该修复什么。但是,在您发布的代码示例中,只需切换到 Task.Delay()
而不是 Thread.Sleep()
就足够了。由于延迟始终不为零,因此该方法将始终在该点产生,即使信号量本身可用。这允许 select
在当前调用中的“工作”完成之前继续对 MethodAsync()
的下一次调用。
通过这种方式,所有任务实际上都按照您最初的预期同时创建。
无论现实世界的代码是什么样子,您要做的是确保在获取信号量后有一个异步操作,以允许该方法实际返回给调用者,以便下一个操作( s) 也可以启动。
关于C#。取消任务时,SemaphoreSlim.WaitAsync 不会抛出 OperationCanceledException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48254614/
我想知道是否SemaphoreSlim调用 Await 时具有优先级之类的东西。 我找不到任何东西,但也许以前有人做过这样的事情。 这个想法是,如果我需要,可以稍后以更高的优先级在信号量上调用等待,并
Documentation说: The SemaphoreSlim class represents a lightweight, fast semaphore that can be used fo
目前,我正在努力实现 SemaphoreSlim 以“锁定”必须是线程安全的方法的“部分”。我的问题是,在没有异常处理过载的情况下实现它是非常困难的。因为在释放“锁”之前抛出异常时,它将永远留在那里。
我创建了以下方法 TestThrottled 来尝试限制我的任务,但它根本没有限制,当我调用 WhenAll 并且此方法都具有相同的耗时时。我做错了什么吗? private static as
我们正在使用 .NET 4.5.1 开发一个应用程序,并在后台使用 SemaphoreSlim 实现了我自己的“异步锁”。要锁定,我们使用以下方法: public async Task LockAsy
semaphore 的真正力量是: Limits the number of threads that can access a resource or pool of resources concu
我在 .NET Core (2.2) 中使用 SemaphoreSlim 类时遇到问题,希望有人可以提供帮助。 我有一个 API 方法 (AddBooking),它将预订添加到数据库,此方法包括 3
概括 我有一个使用 lock 的类(class)提供对私有(private)字段的线程安全访问。但是,由于下面详述的原因,我正在考虑改用 SemaphoreSlim实现线程安全。我知道如果我用 loc
为了同步,我创建了一个 SemaphoreSlim(1)。 这意味着如果我在我的类中有这个信号量的单个实例作为锁: private SemaphoreSlim _initializationSemap
我正在尝试使用新的 SemaphoreSlim .NET 4.0 中的类来限制可以无限期运行的快节奏循环的速率。在对此进行单元测试时,我发现如果循环足够紧密且并行度足够高,SemaphoreSlim当
这个有效: int _counter; readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); async void Button_C
using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp2 { clas
我对 await 关键字的理解是,await 限定语句之后的代码在完成后作为该语句的延续运行。 因此以下两个版本应该产生相同的输出: public static Task Run(Semaph
class Program { static IEnumerable list = Enumerable.Range(1, 10).Select(i => new site(i.ToStrin
我在 Async/Await 函数中有一部分代码,我希望一次只执行一个线程。 这相对简单,只需创建一个新的 SemaphoreSlim(1) 并使用 WaitAsync/Release。效果是第一个线
根据文档: "a SemaphoreSlim doesn't use a Windows kernel semaphore". SemaphoreSlim 是否使用了任何特殊资源,这使得在不再使用 S
SemaphoreSlim 的文档说“只有在所有其他操作都已完成时才应使用 Dispose”。 应该如何调整以下类,以便线程 B 可以在线程 A 等待 Async() 时调用 Dispose()。调用
附加问题 SemaphoreSlim(1, 1) 是否仍然确保我有正确的输出 1000000,即使 task1 和 task2 在 2 个不同的平台上运行核心? 原始问题 考虑以下代码片段,_sema
我最近开始学习 C#,但在更新 UI 时遇到了运行多线程的问题。根据我目前所学,SemaphoreSlim 似乎是运行多线程同时仍控制最大并发线程数的正确方法。 场景:我想向网站(例如http://w
我遇到了一个问题,我需要限制对另一个网络服务器的调用次数。它会有所不同,因为服务器是共享的,并且可能具有更多或更少的容量。 我正在考虑使用 SemaphoreSlim 类,但没有公共(public)属
我是一名优秀的程序员,十分优秀!