When the user clicks a certain part of my form, I need to start updating another area of the same form. This is done using a separate task. Thing is, if an update is triggered and there is an update in progress, I need to cancel it, wait for it to finish, and launch a new one.
I am new to TPL and cannot use .NET 5 yet due to time constraints (we have a VS2012 license but have yet to migrate our solution). Here's the code:
private mLoadGridLock = new Object();
private CancellationTokenSource mCancellationTokenSource;
private bool mLoadingFrames;
private Task mLoadingTask;
private List<string> mFramesList;
public FrameLoader(){
InitializeComponent();
mLoadingFrames = false;
mCancellationTokenSource = new CancellationTokenSource();
}
public void LoadGrid(){
lock (mLoadGridLock) {
if (mStudiesGrid.RowCount <= 0)
return;
Debug.WriteLine("--->LoadGrid Called");
if (mLoadingFrames) {
mCancellationTokenSource.Cancel();
Debug.WriteLine("--->Waiting for cancellation...");
mLoadingTask.Wait();
Debug.WriteLine("--->Wait for cancellation over!");
}
Debug.WriteLine("--->Launching new task");
mLoadingTask = Task.Factory.StartNew(() => LoadFrames(), mCancellationTokenSource.Token);
}
}
private void LoadFrames(){
mLoadingFrames = true;
Debug.WriteLine(" +++> Loader task started!");
int i = 0;
BeginInvoke(() => flp.Controls.Clear());
mFramesList = FrameRules.GenerateFrameList(this);
if (mFramesList <= 0)
return;
foreach (string framePath in mFramesList) {
if (mCancellationTokenSource.Token.IsCancellationRequested) {
mLoadingFrames = false;
Debug.WriteLine(" +++>Loader task cancelled");
return;
}
Debug.WriteLine(" +++>Loading frame " + i.ToString());
i = i + 1;
FrameOfReference fofr = new FrameOfReference();
fofr.BackgroundImage = My.Resources.Loading;
BeginInvoke(() => flp.Controls.Add(fofr));
Thread.Sleep(3000);
BeginInvoke(() => fofr.BackgroundImage == My.Resources.Done);
}
mLoadingFrames = false;
Debug.WriteLine(" +++>Loader task finished");
}
This runs fine the first time, but on the second one I get a deadlock. This is the debug output, which is consistent (it's always the same):
--->LoadGrid Called
--->Launching new task
+++> Loader task started!
+++>Loading frame 0
+++>Loading frame 1
The thread 'ShowMessage-Execute' (0x168c) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0x168c) has exited with code 0 (0x0).
+++>Loader task finished
--->LoadGrid Called
--->Launching new task
+++> Loader task started!
+++>Loading frame 0
--->LoadGrid Called
--->Waiting for cancellation...
It's clear that the problem is caused by LoadGrid being called twice on the second time I trigger an update; that's why I added the lock
block. But this still hangs... This is my first shot at using TPL, so any input is welcome.
UPDATE: After changing lock(this)
to lock(mLoadGridLock)
, the deadlock disappeared, but on the second or third attempt, it doesn't load any frame and from then onwards it loads none. Here's a log:
--->LoadGrid Called
--->Launching new task
+++> Loader task started!
+++>Loading frame 0
The thread 'ShowMessage-Execute' (0xe50) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0xe50) has exited with code 0 (0x0).
+++>Loading frame 1
+++>Loader task finished
--->LoadGrid Called
--->Launching new task
+++> Loader task started!
+++>Loading frame 0
--->LoadGrid Called
--->Waiting for cancellation...
+++>Loader task cancelled
--->Wait for cancellation over!
--->Launching new task