Handling CancellationToken from different class - c#

I have a class like so:
public class FtpTaskVideo : IFtpTask
{
//some fields
public CancellationTokenSource tokenSource = new CancellationTokenSource();
private Panel CreatePanel(string text, int count, int value)
{
Panel pnlOutput = new Panel();
pnlOutput.Name = "pnlInfo";
pnlOutput.AutoSize = true;
pnlOutput.BorderStyle = BorderStyle.FixedSingle;
//adding some controls
Button btnUserCancel = new Button();
btnUserCancel.Name = "btnUserCancel";
btnUserCancel.AutoSize = true;
btnUserCancel.Text = "Stop";
btnUserCancel.Click += new EventHandler(btnUserCancel_Click);
pnlOutput.Controls.Add(btnUserCancel);
btnUserCancel.BringToFront();
return pnlOutput;
}
public void btnUserCancel_Click(object sender, EventArgs e)
{
tokenSource.Cancel();
}
public void Start()
{
//some code
while(somethingToDownload)
{
var task = Task<SharedConstants.downloadFtpFileStatus>.Factory.StartNew(() => dff.Download(tokenSource.Token), tokenSource.Token);
try
{
downloadStatus = task.Result;
}
catch (System.AggregateException exc)
{
//do something
}
//some code
}
}
And in the second class (dff):
public Shared.Classes.SharedConstants.downloadFtpFileStatus Download(CancellationToken token)
{
if (token.IsCancellationRequested)
{
return Shared.Classes.SharedConstants.downloadFtpFileStatus.CANCELLED;
}
else //do some stuff
}
Now, I have another class, which dff is an instance of and Download is it's method. One of the things dff does is update and redraw the panel according to the data it gets during Download method operation. How, after it draws a button and I press it can I send the cancel token back to original class to stop it from downloading?

Related

C# WPF Threading : How to Stop the newly created thread in a event function(Click on a button). without affecting the main thread

Here in the below code I want to stop the thread which is created in StartInvokeExplorer function. Also the starter function in the StartInvokeExplorer is a keyhook function.
public void InvokeExplorerStart_Click(object sender, RoutedEventArgs e)
{
Automate.IsInvokeExplorerClicked = true;
if (InvokeExplorer.Content.Equals("InvokeExplorerStart"))
{
InvokeExplorer.Content = "InvokeExplorerStop";
StartInvokeExplorer();
//InvokeExplorer.Dispatcher.BeginInvoke(new InvokeExplorerDelegate(StartInvokeExplorer));
}
else
{
InvokeExplorer.Content = "InvokeExplorerStart";
StopInvokeExplorer();
}
}
public void StartInvokeExplorer()
{
if (XmlDataGrid.SelectedCells.Count > 0)
{
StartupCount = 1;
thread = new Thread(() =>
{
Starter(StartupCount);
});
thread.IsBackground = true;
thread.Start();
}
else
{
MessageBox.Show("Please select the recorded row to fetch the new data ");
InvokeExplorer.Content = "InvokeExplorerStart";
}
}
private void Starter(int cnt)
{
try
{
if (cnt > 0)
{
Hook.GlobalEvents().MouseClick += (sender, e) =>
{
if (e.Button == MouseButtons.Left)
{
Automate.Show(e);
}
};
Hook.GlobalEvents().MouseDoubleClick += (sender, e) =>
{
Automate.IsDoubleClick = true;
Automate.Show(e);
Automate.IsDoubleClick = false;
};
System.Windows.Forms.Application.Run(new ApplicationContext());
}
else
{
Hook.GlobalEvents().Dispose();
}
}
catch (Exception ex)
{
ErrorLog.Log(ex);
}
}
As from what I have understand, you want to stop the running thread.
This is how.
First, you need to create some stop logic. In your case, it would be some variable, like:
bool threadShouldRun;
and then inside your thread function, you should create a loop like:
void MyThreadFunc()
{
while(threadShouldRun)
{
threadWork();
Thread.Sleep(100);
}
}
When you want to stop the thread, just set your threadShouldRun variable to false.
Sleep is needed here. Without this, thread may use 100% of processor core.
You can use an AutoResetEvent in conjunction with a CancellationToken. Something along the line of (code not tested)
CancellationTokenSource cts;
AutoResetEvent autoResetEvent;
Thread thread;
public void ThreadStart()
{
cts = new CancellationTokenSource();
autoResetEvent = new AutoResetEvent();
thread = new Thread(new ParameterizedThreadStart(ThreadJob));
thread.Start(cts.Token);
}
public void ThreadStop()
{
cts?.Cancel();
thread?.Join();
cts?.Dispose();
autoResetEvent?.Dispose();
}
public static void ThreadJob(object obj)
{
var ct = (CancellationToken)obj;
while (!ct.IsCancellationRequested)
{
if(WaitHandle.WaitAny(new[] { tc.WaitHandle, autoResetEvent}) == 1)
{
// Do your stuff
}
}
}
public void PerformJobInThread()
{
autoResetEvent?.Set();
}
This way your thread will run until you call the ThreadStop method (actually, until you cancel your CancellationTokenSource) but you can still control when to "enable" it.

How to call a function in a backgroundworker thread that is to be completed on the main UI thread? [duplicate]

So, first I have read a ton of threads on this particular problem and I still do not understand how to fix it. Basically, I am trying to communicate with a websocket and store the message received in an observable collection that is bound to a listview. I know that I am getting a response back properly from the socket, but when it tries to add it to the observable collection it gives me the following error:
The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))
I've read some information on "dispatch" as well as some other things, but I am just massively confused! Here is my code:
public ObservableCollection<string> messageList { get; set; }
private void MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args)
{
string read = "";
try
{
using (DataReader reader = args.GetDataReader())
{
reader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
read = reader.ReadString(reader.UnconsumedBufferLength);
}
}
catch (Exception ex) // For debugging
{
WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
// Add your specific error-handling code here.
}
if (read != "")
messageList.Add(read); // this is where I get the error
}
And this is the binding:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
//await Authenticate();
Gameboard.DataContext = Game.GameDetails.Singleton;
lstHighScores.ItemsSource = sendInfo.messageList;
}
How do I make the error go away while still binding to the observable collection for my listview?
This solved my issue:
Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
// Your UI update code goes here!
}
);
Correct way to get the CoreDispatcher in a Windows Store app
Try replacing
messageList.Add(read);
with
Dispatcher.Invoke((Action)(() => messageList.Add(read)));
If you're calling from outside your Window class, try:
Application.Current.Dispatcher.Invoke((Action)(() => messageList.Add(read)));
Slight modification for task based async methods but the code in here will not be awaited.
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
// Your UI update code goes here!
}
).AsTask();
This code WILL await, and will allow you to return a value:
private async static Task<string> GetPin()
{
var taskCompletionSource = new TaskCompletionSource<string>();
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
var pin = await UI.GetPin();
taskCompletionSource.SetResult(pin);
}
);
return await taskCompletionSource.Task;
}
And on Android:
private async Task<string> GetPin()
{
var taskCompletionSource = new TaskCompletionSource<string>();
RunOnUiThread(async () =>
{
var pin = await UI.GetPin();
taskCompletionSource.SetResult(pin);
});
return await taskCompletionSource.Task;
}
Maby this is not a "good" practice, but it works.. I leave a message from webSocket, to mainBody instance, where I have a timered reader...
public class C_AUTHORIZATION
{
public Observer3.A_MainPage_cl parentPageInstance; //еще одни экземпляр родителя
public WebSocket x_Websocket;
private string payload = "";
private DateTime nowMoment = DateTime.Now;
public void GET_AUTHORIZED()
{
bitfinex_Websocket= new WebSocket("wss://*****.com/ws/2");
var apiKey = "";
var apiSecret = "";
DateTime nowMoment = DateTime.Now;
payload = "{}";
x_Websocket.Opened += new EventHandler(websocket_Opened);
x_Websocket.Closed += new EventHandler(websocket_Closed);
}
void websocket_Opened(object sender, EventArgs e)
{
x_Websocket.Send(payload);
parentPageInstance.F_messager(payload);
}
void websocket_Closed(object sender, EventArgs e)
{
parentPageInstance.F_messager("L106 websocket_Closed!");
GET_AUTHORIZED();
}
}
public sealed partial class A_MainPage_cl : Page
{
DispatcherTimer ChartsRedrawerTimer;
public bool HeartBeat = true;
private string Message;
public A_MainPage_cl()
{
this.InitializeComponent();
ChartsRedrawerTimer = new DispatcherTimer() { Interval = new TimeSpan(0, 0, 0, 0, 100) };
ChartsRedrawerTimer.Tick += Messager_Timer;
ChartsRedrawerTimer.Start();
}
private void Messager_Timer(object sender, object e)
{
if(Message !=null) //
{
F_WriteLine(Message);
Message = null; //
}
}
public void F_messager(string message) //
{
Message = message;
}
In Xamarin, I got around this by using:
Device.BeginInvokeOnMainThread(() => {
// code goes here
});

Updating label with data from thread

I am trying to update a label in windows forms. The action is happening in a separate class but while the action is happening on a separate class. The label should be updated, but things seem to be not working. Kindly assist
Below is the Back code of the form ProcessingUI
public partial class ProcessingUI : Form
{
private void start_Click(object sender, EventArgs e)
{
StartProcessingTask();
}
private void StartProcessingTask()
{
if (_isRunning)
return;
_isRunning = true;
_taskToken = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
while (_isRunning)
{
var data = _processing.Processdata(lblCounter, _taskToken);
if (data.Success)
_isRunning = false;
if (_taskToken.IsCancellationRequested)
return;
}
});
}
public delegate void SetStatusCallback();
public void UpdateStatus()
{
lblCounter.Invoke((Action)delegate
{
lblCounter.Text += new EventHandler(_processing.Raise);
});
}
}
Then here is a separate class that has the action, basically its just updating.
Now on update I just want to pass the record that is being updated. So i call the Method from the form and use it in this class.
public class Processing
{
public Results Processdata(CancellationTokenSource taskToken)
{
foreach (var record in dataCases)
{
//Doing other things here like updating
new ProcessingUI().UpdateStatus();//This is the method I am calling from the form.
get(record.RequestReference);
}
}
public string get(string msg)
{
return msg;
}
public void Raise(object sender, EventArgs e)
{
get("hello");
}
}
Using Progress to handle cross-thread access
public class Processing
{
public Results ProcessData(IProgress<string> statusReporter, CancellationToken cancellationToken)
{
foreach (var record in dataCases)
{
// Doing other things here like updating
// Update the status:
statusReporter.OnReport(record.RequestReference);
// Stop if the task has been cancelled:
cancellationToken.ThrowIfCancellationRequested();
}
}
}
public partial class ProcessingUI : Form
{
private void start_Click(object sender, EventArgs e)
{
StartProcessingTask();
}
private void StartProcessingTask()
{
if (_isRunning)
return;
_isRunning = true;
_taskToken = new CancellationTokenSource();
CancellationToken cancellationToken = _taskToken.Token;
IProgress<string> statusReporter = new Progress<string>(UpdateStatus);
Task.Run(() =>
{
while (_isRunning)
{
var data = _processing.ProcessData(lblCounter, cancellationToken);
if (data.Success)
{
_isRunning = false;
}
else
{
cancellationToken.ThrowIfCancellationRequested();
}
}
});
}
private void UpdateStatus(string message)
{
lblCounter.Text = message;
}
}

The application called an interface that was marshalled for a different thread - Windows Store App

So, first I have read a ton of threads on this particular problem and I still do not understand how to fix it. Basically, I am trying to communicate with a websocket and store the message received in an observable collection that is bound to a listview. I know that I am getting a response back properly from the socket, but when it tries to add it to the observable collection it gives me the following error:
The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))
I've read some information on "dispatch" as well as some other things, but I am just massively confused! Here is my code:
public ObservableCollection<string> messageList { get; set; }
private void MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args)
{
string read = "";
try
{
using (DataReader reader = args.GetDataReader())
{
reader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
read = reader.ReadString(reader.UnconsumedBufferLength);
}
}
catch (Exception ex) // For debugging
{
WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
// Add your specific error-handling code here.
}
if (read != "")
messageList.Add(read); // this is where I get the error
}
And this is the binding:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
//await Authenticate();
Gameboard.DataContext = Game.GameDetails.Singleton;
lstHighScores.ItemsSource = sendInfo.messageList;
}
How do I make the error go away while still binding to the observable collection for my listview?
This solved my issue:
Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
// Your UI update code goes here!
}
);
Correct way to get the CoreDispatcher in a Windows Store app
Try replacing
messageList.Add(read);
with
Dispatcher.Invoke((Action)(() => messageList.Add(read)));
If you're calling from outside your Window class, try:
Application.Current.Dispatcher.Invoke((Action)(() => messageList.Add(read)));
Slight modification for task based async methods but the code in here will not be awaited.
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
// Your UI update code goes here!
}
).AsTask();
This code WILL await, and will allow you to return a value:
private async static Task<string> GetPin()
{
var taskCompletionSource = new TaskCompletionSource<string>();
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
var pin = await UI.GetPin();
taskCompletionSource.SetResult(pin);
}
);
return await taskCompletionSource.Task;
}
And on Android:
private async Task<string> GetPin()
{
var taskCompletionSource = new TaskCompletionSource<string>();
RunOnUiThread(async () =>
{
var pin = await UI.GetPin();
taskCompletionSource.SetResult(pin);
});
return await taskCompletionSource.Task;
}
Maby this is not a "good" practice, but it works.. I leave a message from webSocket, to mainBody instance, where I have a timered reader...
public class C_AUTHORIZATION
{
public Observer3.A_MainPage_cl parentPageInstance; //еще одни экземпляр родителя
public WebSocket x_Websocket;
private string payload = "";
private DateTime nowMoment = DateTime.Now;
public void GET_AUTHORIZED()
{
bitfinex_Websocket= new WebSocket("wss://*****.com/ws/2");
var apiKey = "";
var apiSecret = "";
DateTime nowMoment = DateTime.Now;
payload = "{}";
x_Websocket.Opened += new EventHandler(websocket_Opened);
x_Websocket.Closed += new EventHandler(websocket_Closed);
}
void websocket_Opened(object sender, EventArgs e)
{
x_Websocket.Send(payload);
parentPageInstance.F_messager(payload);
}
void websocket_Closed(object sender, EventArgs e)
{
parentPageInstance.F_messager("L106 websocket_Closed!");
GET_AUTHORIZED();
}
}
public sealed partial class A_MainPage_cl : Page
{
DispatcherTimer ChartsRedrawerTimer;
public bool HeartBeat = true;
private string Message;
public A_MainPage_cl()
{
this.InitializeComponent();
ChartsRedrawerTimer = new DispatcherTimer() { Interval = new TimeSpan(0, 0, 0, 0, 100) };
ChartsRedrawerTimer.Tick += Messager_Timer;
ChartsRedrawerTimer.Start();
}
private void Messager_Timer(object sender, object e)
{
if(Message !=null) //
{
F_WriteLine(Message);
Message = null; //
}
}
public void F_messager(string message) //
{
Message = message;
}
In Xamarin, I got around this by using:
Device.BeginInvokeOnMainThread(() => {
// code goes here
});

Accessing WebBrowsers From Threadpool

I'm trying to access information on a web browser from another thread. When trying to access the browser.DocumentTitle, I get this error:
The name DocumentTitle does not exist in the current context
I can successfully navigate to webpages inside the DoWork or ProcessWebPage methods but I cannot access the GetTitle function without crashing. I have been working on this part alone for days and simply cannot figure it out.
Here is the problem code:
BROWSER CODE
class BrowserInterface : Form
{
WebBrowser browser;
Thread thread;
State state;
public State State { get { return state; } }
public BrowserInterface()
{
Initialize();
}
void Initialize()
{
browser = new WebBrowser();
state = State.Null;
state = State.Initializing;
thread = new Thread(StartThread);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
while (state == State.Initializing) Thread.Sleep(20);
}
void StartThread()
{
browser = new WebBrowser();
browser.Dock = DockStyle.Fill;
browser.Name = "webBrowser";
browser.ScrollBarsEnabled = false;
browser.TabIndex = 0;
browser.DocumentCompleted +=
new WebBrowserDocumentCompletedEventHandler(this.Web_Completed);
Form form = new Form();
form.Controls.Add(browser);
form.Name = "Browser";
state = State.Null;
Application.Run(form);
}
public void Navigate(string url)
{
state = State.Navigating;
if (browser.IsDisposed)
Initialize();
browser.Navigate(url);
}
public string GetTitle()
{
if (InvokeRequired)
{
BeginInvoke(new MethodInvoker(() => GetTitle()));
}
return browser.DocumentTitle;
}
private void Web_Completed(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var br = sender as WebBrowser;
if (br.Url == e.Url)
state = State.Completed;
}
}
enum State
{
Initializing,
Null,
Navigating,
Completed
}
OTHER THREAD
class Controller
{
public int ThreadsAllowed;
private ManualResetEvent[] resetEvent;
private BrowserInterface[] browser;
static Thread mainThread;
bool run;
bool exit;
public Controller(int threadsAllowed)
{
ThreadsAllowed = threadsAllowed;
resetEvent = new ManualResetEvent[ThreadsAllowed];
browser = new BrowserInterface[ThreadsAllowed];
for (int i = 0; i < ThreadsAllowed; i++)
{
resetEvent[i] = new ManualResetEvent(true);
browser[i] = new BrowserInterface();
}
ThreadPool.SetMaxThreads(ThreadsAllowed, ThreadsAllowed);
mainThread = new Thread(RunThread);
mainThread.Start();
run = false;
exit = false;
}
public void Run()
{
run = true;
}
void RunThread()
{
while (true)
{
while (!run) Thread.Sleep(20);
while (mode == ScoutMode.Off) Thread.Sleep(100);
//wait for the last set to complete
WaitHandle.WaitAll(resetEvent);
if (exit)
break;
for (int i = 0; i < ThreadsAllowed; i++)
ThreadPool.QueueUserWorkItem(DoWork, i);
}
}
void DoWork(object o)
{
int i = (int)o;
if(browser[i].state == State.null)
{
…
… navigation code that works …
…
return;
}
else if(browser[i].state == State.Completed)
ProcessWebPage(i);
}
void ProcessWebPage(int i)
{
string title;
try
{
title = browser[i].GetTitle();
}
catch { return; }
}
}
What hurts my eye is your GetTitle function. When using MethodInvoker, you're dealing with methods of void type, that is, you cannot get return value from the function. That's why you need a delegate which will return you the value.
Also, you have to have else statement, so to not try to return the value when invoking is in fact required.
class BrowserInterface : Form
{
/* ... */
private delegate string StringDelegate();
public string GetTitle()
{
/*
if (InvokeRequired)
{
BeginInvoke(new MethodInvoker(() => GetTitle()));
}
return browser.DocumentTitle;
*/
if (InvokeRequired)
{
object result = Invoke(new StringDelegate(GetTitle));
return (string)result;
}
else
return browser.DocumentTitle;
}
/* ... */
}
At first, use browsers invoke instead of forms one. And the main problem that after invokation you will return to code and try to access browser.DocumentTitle as background thread. To avoid this, add else construction.
public string GetTitle()
{
if (this.browser.InvokeRequired)
{
this.browser.Invoke(new MethodInvoker(() => GetTitle()));
}
else
{
return browser.DocumentTitle;
}
}

Categories

Resources