小编The*_*ias的帖子

如何通过单键查找优化 C# 字典的更新?

比如说我有:

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 项几何信息,并且查找位于内部循环中。请不要给出“过早优化是万恶之源”的答案。:)

是的,我已经简介了。

截屏

c# optimization performance dictionary

6
推荐指数
1
解决办法
146
查看次数

如果目录内容在迭代过程中发生变化,Directory.EnumerateFiles 会发生什么?

我已经阅读了关于Directory.EnumerateFiles和之间差异的讨论Directory.GetFiles

我知道他们在内部都使用 System.IO.FileSystemEnumerableFactory.CreateFileNameIterator()

不同之处在于EnumerateFiles可能会使用延迟执行(lazy),而会使用GetFiles()a ToArray,因此该函数已经被执行了。

但是如果在迭代过程中将文件和文件夹添加到字典中会发生什么。迭代是否只迭代在EnumerateFiles()?期间存在的项目?

更糟糕的是:如果在迭代过程中删除文件会发生什么:它们还会被迭代吗?

c# file enumerate system.io.directory getfiles

6
推荐指数
1
解决办法
1504
查看次数

某处是否有 Ix.NET (System.Interactive) 的示例?

我有一个异步方法,说:

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)

c# linq lazy-evaluation system.interactive rx.net

6
推荐指数
1
解决办法
2099
查看次数

Task.Run 中的异步 lambda 与常规 lambda

假设我有一个方法可以运行连续的 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# task-parallel-library async-await

6
推荐指数
1
解决办法
2869
查看次数

如何实现一个高效的 WhenEach 来传输 IAsyncEnumerable 的任务结果?

我正在尝试使用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

6
推荐指数
2
解决办法
1148
查看次数

ChannelReader&lt;T&gt; 和 IAsyncEnumerable&lt;T&gt; 之间的区别

我会保持这个简短

阅读“在 ASP.NET Core SignalR 中使用流式传输”一文(第一部分,服务器到客户端流式传输)后, ChannelReader[T]IAsyncEnumerable[T]之间有什么区别?

c# asp.net-core system.threading.channels

6
推荐指数
1
解决办法
388
查看次数

在返回带有取消的 IAsyncEnumerable 的函数中迭代 IAsyncEnumerable

正如标题所说,我必须执行以下功能:

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)

我很难理解取消令牌的去向,并且有一种挥之不去的感觉,我在太多地方使用了它。

当您解构所有奇特的异步内容时,这里实际上发生了什么?还有没有更好的方法来编写这个函数?

c# async-await c#-8.0 iasyncenumerable ef-core-3.0

6
推荐指数
1
解决办法
4615
查看次数

等待取消任务的简洁方法?

我发现自己经常编写这样的代码:

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()方法什么的。

c# task cancellation async-await cancellation-token

6
推荐指数
2
解决办法
2361
查看次数

结合 Interlocked.Increment 和 Volatile.Write

我一直在查看 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)

c# concurrency volatile

6
推荐指数
1
解决办法
113
查看次数

如何更新字典中键的值,但前提是该键已经存在,而不需要对键进行两次哈希处理?

我有一个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# performance dictionary hashcode .net-6.0

6
推荐指数
1
解决办法
1406
查看次数