【发布时间】:2020-03-19 10:57:08
【问题描述】:
我试图了解async/await 在 C# 中的工作原理。我创建了以下示例。
public class SynchronizedCache<TKey, TValue>
{
private readonly System.Threading.ReaderWriterLockSlim _lock
= new System.Threading.ReaderWriterLockSlim();
private readonly IDictionary<TKey, TValue> _dictionary
= new Dictionary<TKey, TValue>();
public TValue this[TKey key]
{
get
{
_lock.EnterReadLock();
try
{
if (!_dictionary.TryGetValue(key, out var value))
{
throw new InvalidOperationException($"key {key} doesn't exist");
}
return value;
}
finally
{
_lock.ExitReadLock();
}
}
set
{
_lock.EnterWriteLock();
_dictionary[key] = value;
_lock.ExitWriteLock();
}
}
public bool TryGetValue(TKey key, out TValue value)
{
try
{
value = this[key];
return true;
}
catch (Exception)
{
value = default(TValue);
return false;
}
}
public void SetValue(TKey key, TValue value)
{
this[key] = value;
}
}
class Program
{
private static readonly SynchronizedCache<int, string> Cache
= new SynchronizedCache<int, string>();
private static readonly Random Random = new Random();
private static readonly IList<Task> Readers = new List<Task>();
private static readonly IList<Task> Writers = new List<Task>();
static async Task ReaderTask(string name)
{
while (true)
{
lock (Random)
{
var index = Random.Next(1, 100);
if (Cache.TryGetValue(index, out var value))
{
Console.WriteLine(
$"Reader({name} {Thread.CurrentThread.ManagedThreadId}"
+ $", cache[{index}]={value}");
}
}
await Task.Delay(1000);
}
}
static async Task WriterTask(string name)
{
while (true)
{
lock (Random)
{
var index = Random.Next(1, 100);
var value = Random.Next(100, 1000);
Cache[index] = value.ToString();
Console.WriteLine(
$"Writer({name}) {Thread.CurrentThread.ManagedThreadId}"
+ $", cache[{index}]={Cache[index]}");
}
await Task.Delay(500);
}
}
private static async Task Main(string[] args)
{
Console.WriteLine($"Main thread {Thread.CurrentThread.ManagedThreadId}");
await Task.WhenAll(
// readers
ReaderTask("Reader 1"),
ReaderTask("Reader 2"),
ReaderTask("Reader 3"),
ReaderTask("Reader 4"),
// writers
WriterTask("Writer 1"),
WriterTask("Writer 2"));
}
}
当我运行上面的代码时,我得到以下输出:
Main thread 1
Writer(Writer 1) 1, cache[80]=325
Writer(Writer 2) 1, cache[28]=550
Writer(Writer 2) 4, cache[95]=172
Writer(Writer 1) 5, cache[71]=132
Writer(Writer 2) 5, cache[67]=454
Writer(Writer 1) 5, cache[91]=314
Writer(Writer 2) 5, cache[39]=154
Writer(Writer 1) 5, cache[99]=921
Writer(Writer 2) 6, cache[56]=291
Writer(Writer 1) 5, cache[8]=495
Writer(Writer 2) 6, cache[74]=907
Writer(Writer 1) 5, cache[35]=101
Writer(Writer 2) 5, cache[11]=449
Writer(Writer 1) 6, cache[64]=932
Writer(Writer 2) 7, cache[34]=825
Writer(Writer 1) 5, cache[88]=869
Reader(Reader 1 4, cache[8]=495
Writer(Writer 2) 8, cache[43]=983
Writer(Writer 1) 6, cache[15]=590
Writer(Writer 2) 5, cache[22]=276
Writer(Writer 1) 6, cache[58]=845
我知道我的Main() 线程与ThreadId 1 在一起,但我不明白其他线程在哪里创建?我没有在这里创建任何特定的线程。调用异步函数是否会创建已在线程池线程上调度的任务?
【问题讨论】:
-
ReadWriter 和 C# 中的大多数锁不能用于同步任务。您应该寻找自定义实现。 SemaphoreSlim 可用于任务。任务只是一个工作单元,不是线程,不应该被视为一个,它们不并行运行,它们可以在单个 UI 线程上运行,它更像是游戏中的协程。
-
@eocron “任务只是不是线程的工作单元,..”这正是我的理解。但是当我查看控制台输出时,我对到底发生了什么感到困惑。
-
Microsoft Docs 上的文章 Async In Depth 让您了解会发生什么(特别是在“深入了解任务和任务
以实现 CPU 绑定操作”部分) -
Stack Overflow 上已经有大量讨论,详细解释了它是如何工作的。少数见标记重复。短版:
await必须返回执行somewhere。在控制台程序中,“某处”是线程池中的线程。因此线程 ID 是多种多样的。在其他情况下,例如 Winforms,除非另有说明,否则恢复执行的上下文是主 UI 线程,因此在这种情况下您会看到相同的线程 ID。
标签: c# async-await