我试图了解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
一起使用,但是我不知道其他线程是在哪里创建的?我不在这里创建任何特定的线程。调用异步函数是否会创建已在线程池线程上调度的任务?
答案 0 :(得分:1)
这是对async
的滥用。在示例中,您的任务受计算约束,异步执行不会给您带来任何好处。
此外,为此将方法标记为async
或创建新的Task
并不能保证将启动新的线程来为其服务。提供时,目前只有TaskCreationOptions.LongRunning
使用新线程,但这严格是实现细节,如有更改,恕不另行通知。
当调用真正的异步方法(例如,从网络或文件中读取)时,该方法将一直执行到无法同步执行的第一个await
。使用环境SynchronizationContext
在该点将连续继续排队。等待的操作完成后,将启动继续操作,如果使用了ThreadPool
或没有环境,则可以在ConfigureAwait(false)
上完成。在所有这些方面,没有什么地方可以保证有新的线程执行。
答案 1 :(得分:-1)
由ThreadPool
类创建ID为4、5、6、7和8的线程,并在每次Task
完成时使用该线程,以运行此{{1 }}。可以随时通过查询属性ThreadPool.ThreadCount
获得这些线程的数量:
获取当前存在的线程池线程数。
最初,Task
类创建的线程数量与计算机的内核数量一样。如果ThreadPool
处于饥饿状态,它将以每500毫秒一个的速率创建新线程。您可以通过调用方法ThreadPool.SetMinThreads
来控制线程池线程的最小数量。
线程池线程针对粒度工作负载进行了优化。一个线程池线程每秒可以运行数百万个ThreadPool
连续。它也可以运行很长的工作量,持续数秒或数分钟,但是通常您应该避免使用线程池线程那么长时间,以防止Task
饿死。