比如说我有:
Dictionary<string, double> foo;
Run Code Online (Sandbox Code Playgroud)
我可以:
foo["hello"] = foo["hello"] + 2.0
Run Code Online (Sandbox Code Playgroud)
或者我可以这样做:
foo["hello"] += 2.0
Run Code Online (Sandbox Code Playgroud)
...但是编译器只是将其扩展为上面的代码。我通过使用 JetBrains .Peek 查看程序集来验证这一点。
这似乎很浪费,因为需要进行两次关键查找才能更新。是否有一种字典实现可以在一次查找中完成此操作?请注意,我使用字典来存储来自网格的 100k 项几何信息,并且查找位于内部循环中。请不要给出“过早优化是万恶之源”的答案。:)
是的,我已经简介了。

我已经阅读了关于Directory.EnumerateFiles和之间差异的讨论Directory.GetFiles。
我知道他们在内部都使用
System.IO.FileSystemEnumerableFactory.CreateFileNameIterator()
不同之处在于EnumerateFiles可能会使用延迟执行(lazy),而会使用GetFiles()a ToArray,因此该函数已经被执行了。
但是如果在迭代过程中将文件和文件夹添加到字典中会发生什么。迭代是否只迭代在EnumerateFiles()?期间存在的项目?
更糟糕的是:如果在迭代过程中删除文件会发生什么:它们还会被迭代吗?
我有一个异步方法,说:
public async Task<T> GetAsync()
{
}
Run Code Online (Sandbox Code Playgroud)
并将从以下位置调用:
public async Task<IEnumerable<T>> GetAllAsync()
{
foreach (var item in something)
{
var result = await GetAsync();
yield return result;
}
}
Run Code Online (Sandbox Code Playgroud)
上面的语法无效,但基本上我是在异步生成器之后。我知道它可以通过 Observables 处理。我对 Rx.NET 进行了实验,并且在一定程度上奏效了。但我试图避免它给代码库带来的复杂性,更重要的是,上述需求本质上仍然不是一个反应式系统(我们的仍然是基于拉取的)。例如,我只会在一段时间内收听传入的异步流,并且我必须从消费者端停止生产者(而不仅仅是取消订阅消费者)。
我可以像这样反转方法签名:
public IEnumerable<Task<T>> GetAllAsync()
Run Code Online (Sandbox Code Playgroud)
但这使得在不阻塞的情况下进行 LINQ 操作有点棘手。我希望它是非阻塞的,并且不将整个内容加载到内存中。这个库:AsyncEnumerable正是我正在寻找的,但如何用Ix.NET做到这一点?它们的用途与我相信的相同。
换句话说,我如何利用 Ix.NETIAsyncEnumerable在处理await? 喜欢,
public async IAsyncEnumerable GetAllAsync()
{
foreach (var item in something)
{
var result = await GetAsync();
return // what?
}
}
Run Code Online (Sandbox Code Playgroud) 假设我有一个方法可以运行连续的 while 循环并进行一些异步调用
async Task MethodA(){
while(true){ perform async/await operations }
}
Run Code Online (Sandbox Code Playgroud)
之间有什么区别:
Task.Run( () => MethodA(); }
Task.Run( async () => await MethodA(); }
Run Code Online (Sandbox Code Playgroud)
如果有区别,什么时候一个比另一个更有用?Task.Run 是否以不同的方式对待其每个重载?
我正在尝试使用C# 8提供的新工具更新我的工具集,一种似乎特别有用的方法是Task.WhenAll返回一个IAsyncEnumerable. 此方法应在任务结果可用时立即流式传输任务结果,因此命名它WhenAll没有多大意义。WhenEach听起来更合适。该方法的签名是:
public static IAsyncEnumerable<TResult> WhenEach<TResult>(Task<TResult>[] tasks);
Run Code Online (Sandbox Code Playgroud)
这种方法可以这样使用:
var tasks = new Task<int>[]
{
ProcessAsync(1, 300),
ProcessAsync(2, 500),
ProcessAsync(3, 400),
ProcessAsync(4, 200),
ProcessAsync(5, 100),
};
await foreach (int result in WhenEach(tasks))
{
Console.WriteLine($"Processed: {result}");
}
static async Task<int> ProcessAsync(int result, int delay)
{
await Task.Delay(delay);
return result;
}
Run Code Online (Sandbox Code Playgroud)
预期输出:
已处理:5 已
处理:4 已
处理:1 已
处理:3 已
处理:2
我设法Task.WhenAny在循环中使用该方法编写了一个基本实现,但是这种方法存在一个问题:
public static async IAsyncEnumerable<TResult> WhenEach<TResult>(
Task<TResult>[] tasks)
{ …Run Code Online (Sandbox Code Playgroud) c# task-parallel-library async-await c#-8.0 iasyncenumerable
我会保持这个简短
阅读“在 ASP.NET Core SignalR 中使用流式传输”一文(第一部分,服务器到客户端流式传输)后, ChannelReader[T]和IAsyncEnumerable[T]之间有什么区别?
正如标题所说,我必须执行以下功能:
public async IAsyncEnumerable<Job> GetByPipeline(int pipelineId,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
await foreach (var job in context.Jobs.Where(job => job.Pipeline.Id == pipelineId)
.AsAsyncEnumerable()
.WithCancellation(cancellationToken)
.ConfigureAwait(false))
{
yield return job;
}
}
Run Code Online (Sandbox Code Playgroud)
我很难理解取消令牌的去向,并且有一种挥之不去的感觉,我在太多地方使用了它。
当您解构所有奇特的异步内容时,这里实际上发生了什么?还有没有更好的方法来编写这个函数?
我发现自己经常编写这样的代码:
try
{
cancellationTokenSource.Cancel();
await task.ConfigureAwait(false); // this is the task that was cancelled
}
catch(OperationCanceledException)
{
// Cancellation expected and requested
}
Run Code Online (Sandbox Code Playgroud)
鉴于我请求取消,这是预料之中的,而且我真的希望忽略该异常。这似乎是一个常见的案例。
有没有更简洁的方法来做到这一点?我是否错过了有关取消的信息?看来应该有什么task.CancellationExpected()方法什么的。
我一直在查看 System.Reactive 的源代码(这里),它把我带到了这个地方,在同一个变量上有一个Volatile.Read后跟Interlocked.CompareExchange, :
if (Volatile.Read(ref _runDrainOnce) == 0
&& Interlocked.CompareExchange(ref _runDrainOnce, 1, 0) == 0)
{
//do something
}
Run Code Online (Sandbox Code Playgroud)
当我阅读它时,其逻辑是“如果 runDrainOnce 为 0,并且在我将其更改为 1 之前它为零”。这里有什么微妙的吗?为什么第一次检查不是多余的?
(更令人难以置信的是,在同一个函数中有 alock和 a Monitor.Pulse。这是下注的结果吗?:))
整个功能:
private void Schedule()
{
// Schedule the suspending drain once
if (Volatile.Read(ref _runDrainOnce) == 0
&& Interlocked.CompareExchange(ref _runDrainOnce, 1, 0) == 0)
{
_drainTask.Disposable = _scheduler.ScheduleLongRunning(this, DrainLongRunning);
}
// Indicate more work is to be done by the drain loop …Run Code Online (Sandbox Code Playgroud) 我有一个Dictionary<string, decimal>具有固定数量的条目,并且我想经常更新它的许多值,但仅限于已经存在的键。如果字典中尚不存在某个键,我不想添加它,因为我的目标是将字典限制为固定大小。因此,下面的代码(使用set 索引器)将无法满足我的需求:
dictionary[key] = newValue;
Run Code Online (Sandbox Code Playgroud)
key如果它已经存在,此代码将更新它的值,或者如果它不存在,它将插入一个新的键/值对。那不是我想要的。所以我TryUpdate为该类编写了一个扩展方法Dictionary<TKey, TValue>,具有理想的行为:
public static bool TryUpdate<TKey, TValue>(
this Dictionary<TKey, TValue> source,
TKey key, TValue value)
{
ArgumentNullException.ThrowIfNull(source);
if (!source.ContainsKey(key))
return false;
source[key] = value;
return true;
}
Run Code Online (Sandbox Code Playgroud)
现在我的代码按预期工作:
dictionary.TryUpdate(key, newValue);
Run Code Online (Sandbox Code Playgroud)
我不喜欢上述实现的是,如果密钥已经存在(这是我的场景中的常见情况),则密钥将在每个TryUpdate操作中被哈希两次。因此,为了保证我的字典不会增长超过其初始大小,我将在性能方面付出双倍的代价。键是长字符串,因此哈希成本很高。有没有办法重写我的TryUpdate方法,使其与set索引器一样高效?
c# ×10
async-await ×4
c#-8.0 ×2
dictionary ×2
performance ×2
.net-6.0 ×1
asp.net-core ×1
cancellation ×1
concurrency ×1
ef-core-3.0 ×1
enumerate ×1
file ×1
getfiles ×1
hashcode ×1
linq ×1
optimization ×1
rx.net ×1
task ×1
volatile ×1