我有一个复杂的情况,但我会尽量简短,只让重要的细节。我正在尝试实现基于任务的工作处理。这是这门课的内容:
internal class TaskBasedJob : IJob
{
public WaitHandle WaitHandle { get; }
public JobStatus Status { get; private set; }
public TaskBasedJob(Func<Task<JobStatus>> action, TimeSpan interval, TimeSpan delay)
{
Status = JobStatus.NotExecuted;
var semaphore = new SemaphoreSlim(0, 1);
WaitHandle = semaphore.AvailableWaitHandle;
_timer = new Timer(async x =>
{
// return to prevent duplicate executions
// Semaphore starts locked so WaitHandle works properly
if (semaphore.CurrentCount == 0 && Status != JobStatus.NotExecuted)
{
return;
Status = JobStatus.Failure;
}
if(Status != JobStatus.NotExecuted)
await semaphore.WaitAsync();
try
{
await action();
}
finally
{
semaphore.Release();
}
}, null, delay, interval);
}
}
下面是调度程序类:
internal class Scheduler : IScheduler
{
private readonly ILogger _logger;
private readonly ConcurrentDictionary<string, IJob> _timers = new ConcurrentDictionary<string, IJob>();
public Scheduler(ILogger logger)
{
_logger = logger;
}
public IJob ScheduleAsync(string jobName, Func<Task<JobStatus>> action, TimeSpan interval, TimeSpan delay = default(TimeSpan))
{
if (!_timers.ContainsKey(jobName))
{
lock (_timers)
{
if (!_timers.ContainsKey(jobName))
_timers.TryAdd(jobName, new TaskBasedJob(jobName, action, interval, delay, _logger));
}
}
return _timers[jobName];
}
public IReadOnlyDictionary<string, IJob> GetJobs()
{
return _timers;
}
}
在这个库中,我有一个如下的服务:所以这个服务的思想只是在名为
_accessInfos
以及它的异步方法。您可以在构造函数中看到我已经添加了获取数据的作业。
internal class AccessInfoStore : IAccessInfoStore
{
private readonly ILogger _logger;
private readonly Func<HttpClient> _httpClientFunc;
private volatile Dictionary<string, IAccessInfo> _accessInfos;
private readonly IScheduler _scheduler;
private static string JobName = "AccessInfoProviderJob";
public AccessInfoStore(IScheduler scheduler, ILogger logger, Func<HttpClient> httpClientFunc)
{
_accessInfos = new Dictionary<string, IAccessInfo>();
_config = config;
_logger = logger;
_httpClientFunc = httpClientFunc;
_scheduler = scheduler;
scheduler.ScheduleAsync(JobName, FetchAccessInfos, TimeSpan.FromMinutes(1));
}
public IJob FetchJob => _scheduler.GetJobs()[JobName];
private async Task<JobStatus> FetchAccessInfos()
{
using (var client = _httpClientFunc())
{
accessIds = //calling a webservice
_accessInfos = accessIds;
return JobStatus.Success;
}
}
所有这些代码都在另一个库中,我在我的ASP.NET Core 2.1项目中引用了这个库。在创业班我接到这样的电话:
//adding services
...
services.AddScoped<IScheduler, Scheduler>();
services.AddScoped<IAccessInfoStore, AccessInfoStore>();
var accessInfoStore = services.BuildServiceProvider().GetService<IAccessInfoStore>();
accessInfoStore.FetchJob.WaitHandle.WaitOne();
第一次
WaitOne()
方法不起作用,因此不加载数据(
_访问信息
是空的),但是如果我再次刷新页面,我可以看到加载的数据(
_访问信息
不是空的,但有数据)。据我所知
等等()
方法是在我的作业完成之前阻止线程执行。
有人知道为什么吗
等等()
方法不正常或我可能做错了什么?
编辑1:
Scheduler
只存储所有
IJob
-如果主要是为了在运行状况页中显示它们,则将它们放入并发字典中以便以后获取它们。每次我们插入一个新的
TaskBasedJob
在字典中,构造函数将被执行,最后我们使用
Timer
为了在一段时间后重新执行这个作业,但是为了使这个线程安全,我使用了SemaphoreSlim类,并从那里公开
WaitHandle
. 这只适用于我需要将方法从异步转换为同步的少数情况。因为一般情况下,我不会使用它,因为在正常情况下,作业将以异步方式执行。
我期望的是
等等()
应该停止当前线程的执行并等待我的计划作业被执行,然后继续执行当前线程。在我的例子中,当前线程是运行的线程
Configure
方法
StartUp
上课。