这是使用带有监视器和睡眠的异步任务的好习惯吗?
Is this good practice use of an async task with monitor and sleep?
我目前 运行 轮询异步任务(这意味着该异步任务每 1 秒调用一次)。除了我下面的异步任务之外,所有其他项目都需要快速更新。此代码有效,但我想知道这是否是好的做法?
注意:_monitor 只被 DoAsync() 使用
private readonly object _monitor = new object();
private void PolledEverySecond()
{
_ = DoAsync(); // do this every 5 seconds
// Other stuff
GetNetworkState();
GetCurrentVelocity();
GetCurrentPosition();
Etc;
}
private async Task DoAsync()
{
await Task.Run(() =>
{
if (!Monitor.IsEntered(_monitor))
{
try
{
Monitor.Enter(_monitor);
DoStuff();
}
finally
{
Thread.Sleep(5000);
Monitor.Exit(_monitor);
}
}
});
}
监视器背后的意图。Enter/Monitor。退出和 Thread.Sleep(5000)。 DoAsync() 不是每 1 秒调用一次吗?我有一个非常适合更新内容的轮询服务,我的许多 ViewModel 都在使用它。但是,在 DoAsync() 的情况下,每秒轮询一次就太过分了。因此,通过使其异步并使用监视器,DoStuff() 大约每 5-6 秒被调用一次。
您当前的代码有两个问题,Monitor.IsEntered
和 Monitor.Enter
调用之间存在竞争条件,ThreadPool
线程与 Thread.Sleep(5000)
阻塞称呼。阻塞 ThreadPool
个线程不是一个好的做法,因为它可能导致池饱和。饱和的 ThreadPool
无法立即响应工作请求,因此程序响应速度变慢。同样在这种情况下,ThreadPool
必须在池中注入更多线程,从而导致内存消耗增加(每个线程至少需要 内存)。
我的建议是从阻塞 Thread.Sleep
切换到异步(非阻塞)Task.Delay
方法,并且还
从线程仿射 Monitor
切换到线程不可知 SemaphoreSlim
. In order to check the availability and acquire the semaphore as an atomic operation, you could use the Wait
方法,将零毫秒作为参数传递:
private readonly SemaphoreSlim _semaphore = new(1, 1);
private async Task DoAsync()
{
await Task.Run(async () =>
{
bool acquired = _semaphore.Wait(0);
if (acquired)
{
var delayTask = Task.Delay(5000);
try
{
DoStuff();
}
finally
{
await delayTask;
_semaphore.Release();
}
}
});
}
这样 ThreadPool
线程将仅在 DoStuff
执行期间使用,然后将被释放,并可用于其他工作。
在启动 DoStuff
之前创建 Task.Delay(5000)
任务并在之后等待它,具有将 DoStuff
的持续时间包括在 5 秒延迟中的普遍理想效果。如果你不想要这种行为,你可以在同一行中创建和 await
任务:await Task.Delay(5000);
我目前 运行 轮询异步任务(这意味着该异步任务每 1 秒调用一次)。除了我下面的异步任务之外,所有其他项目都需要快速更新。此代码有效,但我想知道这是否是好的做法?
注意:_monitor 只被 DoAsync() 使用
private readonly object _monitor = new object();
private void PolledEverySecond()
{
_ = DoAsync(); // do this every 5 seconds
// Other stuff
GetNetworkState();
GetCurrentVelocity();
GetCurrentPosition();
Etc;
}
private async Task DoAsync()
{
await Task.Run(() =>
{
if (!Monitor.IsEntered(_monitor))
{
try
{
Monitor.Enter(_monitor);
DoStuff();
}
finally
{
Thread.Sleep(5000);
Monitor.Exit(_monitor);
}
}
});
}
监视器背后的意图。Enter/Monitor。退出和 Thread.Sleep(5000)。 DoAsync() 不是每 1 秒调用一次吗?我有一个非常适合更新内容的轮询服务,我的许多 ViewModel 都在使用它。但是,在 DoAsync() 的情况下,每秒轮询一次就太过分了。因此,通过使其异步并使用监视器,DoStuff() 大约每 5-6 秒被调用一次。
您当前的代码有两个问题,Monitor.IsEntered
和 Monitor.Enter
调用之间存在竞争条件,ThreadPool
线程与 Thread.Sleep(5000)
阻塞称呼。阻塞 ThreadPool
个线程不是一个好的做法,因为它可能导致池饱和。饱和的 ThreadPool
无法立即响应工作请求,因此程序响应速度变慢。同样在这种情况下,ThreadPool
必须在池中注入更多线程,从而导致内存消耗增加(每个线程至少需要
我的建议是从阻塞 Thread.Sleep
切换到异步(非阻塞)Task.Delay
方法,并且还
从线程仿射 Monitor
切换到线程不可知 SemaphoreSlim
. In order to check the availability and acquire the semaphore as an atomic operation, you could use the Wait
方法,将零毫秒作为参数传递:
private readonly SemaphoreSlim _semaphore = new(1, 1);
private async Task DoAsync()
{
await Task.Run(async () =>
{
bool acquired = _semaphore.Wait(0);
if (acquired)
{
var delayTask = Task.Delay(5000);
try
{
DoStuff();
}
finally
{
await delayTask;
_semaphore.Release();
}
}
});
}
这样 ThreadPool
线程将仅在 DoStuff
执行期间使用,然后将被释放,并可用于其他工作。
在启动 DoStuff
之前创建 Task.Delay(5000)
任务并在之后等待它,具有将 DoStuff
的持续时间包括在 5 秒延迟中的普遍理想效果。如果你不想要这种行为,你可以在同一行中创建和 await
任务:await Task.Delay(5000);