Reputation: 2773
I'm trying to figure out how it comes that my request to an API call is waiting for my backgroundworkers to be completed.
This is my jobManager:
public class JobManager {
public List<AbstractJob> Jobs;
public JobManager() {
Jobs = new List<AbstractJob>();
}
public int newTestJob() {
var job = new TestJob();
job.Name = "TestJob";
Jobs.Add(job);
return job.Id;
}
public void startJob(int id) {
var job = Jobs.Where(j => j.Id == id).FirstOrDefault();
if (job == null) throw new Exception("not found");
job.Execute();
}
public string statusJob(int id) {
var job = Jobs.Where(j => j.Id == id).FirstOrDefault();
if (job == null) throw new Exception("not found");
return job.Status;
}
public List<AbstractJob> runningJobs() {
return Jobs.Where(j => j.Status == "Running").ToList();
}
}
this is a IJob
public interface IJob {
void Execute();
}
this is AbstractJob
public abstract class AbstractJob : IJob {
static int counter = 0;
protected BackgroundWorker _bw = new BackgroundWorker();
public int Id;
public string Status;
public string Name;
public AbstractJob() {
Interlocked.Increment(ref counter);
Id = counter;
_bw.WorkerReportsProgress = true;
_bw.WorkerSupportsCancellation = true;
_bw.DoWork += new DoWorkEventHandler(bw_DoWork);
_bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
_bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
_bw.ReportProgress(0, "Idle");
}
public virtual string Describe() {
return "Not much is known about this four legged animal!";
}
public void Execute() {
_bw.RunWorkerAsync();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) {
this.Status = e.UserState.ToString();
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
BackgroundWorker worker = sender as BackgroundWorker;
if ((e.Cancelled == true)) {
this.Status = "Canceled";
} else if (!(e.Error == null)) {
this.Status = "Error";
} else {
this.Status = "Done";
}
}
public abstract void bw_DoWork(object sender, DoWorkEventArgs e);
}
And say that this is my job:
public class TestJob : AbstractJob {
public override void bw_DoWork(object sender, DoWorkEventArgs e) {
System.Threading.Thread.Sleep(50000); // representing some actual code that takes time
}
}
So I wanted in my controller to create 10 jobs and return the Id's while the tasks are running in the background.
public IEnumerable<int> Get() {
var ids = new List<int>();
for (int i = 0; i < 10; i++) {
var id = jobm.newTestJob();
jobm.startJob(id);
ids.Add(id);
}
return ids;
}
This all seem to work great, except that the request is waiting on the sleeptimer in the job. although when I place a breakpoint on return ids
in my controller; it hit's it very fast (so the async is working)
Upvotes: 1
Views: 6343
Reputation: 2086
Normally you should have the message: "An asynchronous module or handler completed while an asynchronous operation was still pending." It is considered as dangerous to launch background tasks without awaiting them
You should use the TaskParallelLibrary - async/await do do that and properly wait for all the Tasks to return:
public async Task Get()
{
var tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
tasks.Add(Task.Run(() =>
{
//your code here
}));
}
await Task.WhenAll(tasks);
}
If you are doing FireAndForget you can use the new QueueBackgroundWorkItem from framework 4.5.2.
Also check out this Post. It is better to use the native async methods and to transform the whole chain to async instead of simply doing "Task.Run".
Upvotes: 2