- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
如果一个类有成员 TaskCompletionSource<TResult> m_tcs
生命周期长,如果 Task.WhenAny 被调用 m_tcs.Task
作为其论点之一,当调用次数超过 50,000 次左右时,性能似乎呈指数级下降。
为什么在这种情况下这么慢?可能有一种替代方法可以更快地运行但不使用 4 倍多的内存吗?
我的想法是Task.WhenAny
可能会在 m_tcs.Task
中添加和删除如此多的延续并且在那里的某个地方导致 O(N²) 的复杂性。
通过将 TCS 包装在等待 m_tcs.Task
的异步函数中,我找到了一个性能更高的替代方案。 .它使用大约 4 倍的内存,但在超过 20,000 次迭代后运行速度要快得多。
下面的示例代码(为了获得准确的结果,直接编译和运行 .exe 而不附加调试器)。请注意 WhenAnyMemberTcsDirect
有性能问题,WhenAnyMemberTcsIndirect
是更快的选择,WhenAnyLocalTcs
是比较的基线:
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
public class WithTcs
{
// long-lived TaskCompletionSource
private readonly TaskCompletionSource<bool> m_tcs = new TaskCompletionSource<bool>();
// this has performance issues for large N - O(N^2)
public async Task WhenAnyMemberTcsDirectAsync(Task task)
{
await await Task.WhenAny(task, m_tcs.Task).ConfigureAwait(false);
}
// performs faster - O(N), but uses 4x memory
public async Task WhenAnyMemberTcsIndirectAsync(Task task)
{
await await Task.WhenAny(task, AwaitTcsTaskAsync(m_tcs)).ConfigureAwait(false);
}
private async Task<TResult> AwaitTcsTaskAsync<TResult>(TaskCompletionSource<TResult> tcs)
{
return await tcs.Task.ConfigureAwait(false);
}
// baseline for comparison using short-lived TCS
public async Task WhenAnyLocalTcsAsync(Task task)
{
var tcs = new TaskCompletionSource<bool>();
await await Task.WhenAny(task, tcs.Task).ConfigureAwait(false);
}
}
class Program
{
static void Main(string[] args)
{
show_warning_if_debugger_attached();
MainAsync().GetAwaiter().GetResult();
show_warning_if_debugger_attached();
Console.ReadLine();
}
static async Task MainAsync()
{
const int n = 100000;
Console.WriteLine("Running Task.WhenAny tests ({0:#,0} iterations)", n);
Console.WriteLine();
await WhenAnyLocalTcs(n).ConfigureAwait(false);
await Task.Delay(1000).ConfigureAwait(false);
await WhenAnyMemberTcsIndirect(n).ConfigureAwait(false);
await Task.Delay(1000).ConfigureAwait(false);
await WhenAnyMemberTcsDirect(n).ConfigureAwait(false);
}
static Task WhenAnyLocalTcs(int n)
{
Func<WithTcs, Task, Task> function =
(instance, task) => instance.WhenAnyLocalTcsAsync(task);
return RunTestAsync(n, function);
}
static Task WhenAnyMemberTcsIndirect(int n)
{
Func<WithTcs, Task, Task> function =
(instance, task) => instance.WhenAnyMemberTcsIndirectAsync(task);
return RunTestAsync(n, function);
}
static Task WhenAnyMemberTcsDirect(int n)
{
Func<WithTcs, Task, Task> function =
(instance, task) => instance.WhenAnyMemberTcsDirectAsync(task);
return RunTestAsync(n, function);
}
static async Task RunTestAsync(int n, Func<WithTcs, Task, Task> function, [CallerMemberName] string name = "")
{
Console.WriteLine(name);
var tasks = new Task[n];
var sw = new Stopwatch();
var startBytes = GC.GetTotalMemory(true);
sw.Start();
var instance = new WithTcs();
var step = n / 78;
for (int i = 0; i < n; i++)
{
var iTemp = i;
Task primaryTask = Task.Run(() => { if (iTemp % step == 0) Console.Write("."); });
tasks[i] = function(instance, primaryTask);
}
await Task.WhenAll(tasks).ConfigureAwait(false);
Console.WriteLine();
var endBytes = GC.GetTotalMemory(true);
sw.Stop();
GC.KeepAlive(instance);
GC.KeepAlive(tasks);
Console.WriteLine(" Time: {0,7:#,0} ms, Memory: {1,10:#,0} bytes", sw.ElapsedMilliseconds, endBytes - startBytes);
Console.WriteLine();
}
static void show_warning_if_debugger_attached()
{
if (Debugger.IsAttached)
Console.WriteLine("WARNING: running with the debugger attached may result in inaccurate results\r\n".ToUpper());
}
}
最佳答案
我找到了一个解决方案,它似乎运行得很快(O(N)时间)和大约。相同的内存空间,通过使用成员 CancellationTokenSource m_cts
旁边 TaskCompletionSource
.任何以前调用 set m_tcs
取消/故障/结果需要伴随m_cts.Cancel()
.这当然可以抽象。
解决方案:
public class WithTcs
{
// ... same as above, plus below
private readonly CancellationTokenSource m_cts = new CancellationTokenSource();
public async Task WhenAnyMemberCtsAsync(Task task)
{
var ct = m_cts.Token;
var tcs = new TaskCompletionSource<bool>();
using (ct.Register(() => tcs.TrySetFrom(m_tcs)))
await await Task.WhenAny(task, tcs.Task).ConfigureAwait(false);
}
}
public static class TcsExtensions
{
public static bool TrySetFrom<TResult>(this TaskCompletionSource<TResult> dest, TaskCompletionSource<TResult> source)
{
switch (source.Task.Status)
{
case TaskStatus.Canceled:
return dest.TrySetCanceled();
case TaskStatus.Faulted:
return dest.TrySetException(source.Task.Exception.InnerExceptions);
case TaskStatus.RanToCompletion:
return dest.TrySetResult(source.Task.Result);
default:
return false; // TCS has not yet completed
}
}
}
WhenAnyMemberTcsDirect
幕后发生的事情导致 O(N²) 问题。
关于.net - 为什么 Task.WhenAny 在针对同一个 TaskCompletionSource 多次调用时如此缓慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45246385/
我有一个控制台应用程序,我在其中使用 TPL,当我在任务中点击 whenany 时它会退出。我是线程的新手,有人可以指导我正确的方向吗(我单独执行了 RunJob,它没有异常(exception))。
当将多个已完成的任务传递给 Task.WhenAny 时,Task.WhenAny 会优先选择将返回哪个已完成的任务? 最佳答案 当您想知道确切的行为时,通常可以查看 reference source
考虑一个具有某种生命周期的类。在此生命周期中,一个事件可能会发生任意多次,并且该事件是通过完成任务(在事件发生后更新)来发出信号的。该对象也可能被关闭,结束其生命周期。关闭也通过完成任务发出信号。它也
我有这段代码: var client = new TcpClient(); HttpRequestInfo.AddTimestamp("Connecting"); await Task.WhenAny
我在 ReactiveLists 中保存了一些检查列表,它们具有 ChangeTrackingEnabled = true。我只想在每个列表中至少选中一个项目时才启用我的 OkCommand。 此外,
在下面的代码中: if (await Task.WhenAny(task, Task.Delay(100)) == task) { success = true
我想同时执行多个异步任务。每个任务将运行一个 HTTP 请求,该请求可以成功完成或引发异常。我需要等待直到第一个任务成功完成,或者直到所有任务都失败。 如何实现 Task.WhenAny 的重载接受谓
当我使用 Task.WhenAll() 函数并在任务中抛出异常时,会抛出一个新的 AggregateException,我可以捕获它以查看任务中发生的所有异常。但是,当我使用 Task.WhenAny
在我的应用程序中,我正在创建一些并发的网络请求,当其中任何一个完成时我都很满意,所以我使用了方法 Task.WhenAny : var urls = new string[] { "https
假设我有三个任务,a、b 和 c。这三个都保证在 1 到 5 秒之间的随机时间抛出异常。然后我写了下面的代码: await Task.WhenAny(a, b, c); 这最终会从最先出错的任务中抛出
我有以下代码: List> tasks = tasksQuery.ToList(); while (tasks.Any()) { Task completedTask = await Task
我需要为移动应用程序中的任务调用添加超时功能。我尝试使用 Task.WhenAny 来完成此操作,如下所示。这将返回首先完成的任务。我的问题是,最初我是从这个任务中获取返回值的,如果任务没有超时,我仍
我有以下代码片段来处理 Azure 通知中心推送通知: var alert = "{\"aps\":{\"alert\":\"" + message + "\"}}"; var task = Azur
我有一个带有两个 WhenAnyValues 的对象: this.WhenAnyValue(x => x.SomeInt) .Select(x => "Bar" + x) .ToPro
我的解决方案中有两个项目:WPF 项目和类库。 在我的类库中: 我有一个符号列表: class Symbol { Identifier Identifier {get;set;}
我有一个带有两个 WhenAnyValues 的对象: this.WhenAnyValue(x => x.SomeInt) .Select(x => "Bar" + x) .ToPro
我如何并行运行 2 个任务并在一个特定任务结束时得到通知(在我的例子中是列表中的第一个任务) 目前我正在使用这段代码: await Task.WhenAny(A.Play(cancel), B.Pla
我有一个创建多个基于 I/O 的任务的解决方案,我正在使用 Task.WhenAny() 来管理这些任务。但往往很多任务会因为网络问题或请求节流等原因而失败。 在使用 Task.WhenAny() 方
考虑在 ReactiveUI 中使用继承。我有带有 DoSomethingCommand 的 ViewModel 基类。此命令的“CanExecute”取决于属性 Prop1 public class
Reactive UI 中有几种扩展方法可用于获取属性更改的 observable。 我想我明白 WhenAny和 WhenAnyValue . WhenAny用于一系列属性更改通知,您希望其中的对象
我是一名优秀的程序员,十分优秀!