I would like to cancel a Task.run in a clean and simple way here is my code:
bool NTAG_isHere = false;
// CODE (...)
private async Task Dump_NTAG(object sender, EventArgs eventArgs)
{
// while NTAG PRESENT DUMP START:
await Task.Run(() => {while (NTAG_isHere()) { } });
// CODE (...)
// If NTAG NOT PRESENT or not detected stop the dump and kill thread:
if(!NTAG_isHere)
{
// kill the thread
}
{
Thank you,
Edit 4, my complete method :
private void Dump_NTAG(object sender, EventArgs eventArgs)
{
try
{
richTextBox_debug.Invoke(new UpdateTextCallback(CleanText), new object[] {string.Empty});
WriteLine(NTAG_isHere());
if (!NTAG_isHere())
{
Task.Factory.StartNew(() =>
{
richTextBox_debug.Invoke(new UpdateTextCallback(CleanText), new object[] { string.Empty });
byte[] dump = new byte[540];
int i;
richTextBox_debug.Invoke(new UpdateTextCallback(UpdateText), new object[] { "START" + Environment.NewLine + Environment.NewLine });
for (i = 0; i < 135; i++)
{
string Result = arduino.SendCommand("/READ " + i);
string[] SplitResult = Result.Split('/', ' ');
if (SplitResult.Length > 1)
{
if (Result.Split('/', ' ')[1] == "ERROR")
richTextBox_debug.Invoke(new UpdateTextCallback(UpdateText), new object[] { string.Format("NFC_Error" + Result.Substring(1) + Environment.NewLine) });
else
richTextBox_debug.Invoke(new UpdateTextCallback(UpdateText), new object[] { string.Format("NFC_Unknown_Response" + Result + Environment.NewLine) });
i = 135;
}
else
{
string page = "Page";
richTextBox_debug.Invoke(new UpdateTextCallback(UpdateText), new object[] { string.Format(page + " {0} : {1}", i, Result) + Environment.NewLine });
}
}
if (i == 135) { richTextBox_debug.Invoke(new UpdateTextCallback(UpdateText), new object[] { "Dump success !" + Environment.NewLine + Environment.NewLine }); }
arduino.SendCommand("/NTAG_HALT");
arduino.Close();
});
}
else
{
MessageBox.Show("ERROR !!");
}
}
catch (Exception e)
{
WriteLine(e.Message);
}
}
my concern is that i can no longer dump, the boolean would still be wrong with this new code, but my method NTAG_isHere () returns good value, so is not in question. and that never do I pass in MessageBox.Show ("ERROR !!"); with or without ntag on the player
looking at your code now , you can simply do this , there is no need of cancellation thing , just check the method returns true than run task else dont
private void Read_amiibo(object sender, EventArgs eventArgs)
{
bool ishere = NTAG_isHere();
//store reference of local thread
if(ishere)
{
Task.Factory.StartNew(() =>
{
//do operation
}
}
else {
//exit
}
}
one point to add here : if you make use of async/await your call get return when await encounters and code below will not get executed
Below solution will work in case when there is no async/await
make use of CancellationTokenSource with task that will do for you, CancellationToken you can pass as argument to run method and monitor it for cancellation.
This is cleaner way given by Microsoft team.
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var t = Task.Run( () => {
//perform task
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
} , token);
if(!NTAG_isHere)
{
// kill the thread
tokenSource.Cancel();
}
other way not much recommended ,
//store reference of local thread
Thread threadToCancel = null;
Task.Factory.StartNew(() =>
{
//Capture the thread
threadToCancel = Thread.CurrentThread;
}
//you can put timer also , means check value after some time to avoid while loop
while(true) {
if(NTAG_isHere){
threadToCancel.Abort();
}
}
Discussed this approach here : Abort/Cancel Task
reference check here : Task.Run Method (Action, CancellationToken)
Related
I implemented Task synchronization using Monitor in C#.
However, I have read Monitor should not be used in asynchronous operation.
In the below code, how do I implement Monitor methods Wait and PulseAll with a construct that works with Task (asynchronous operations).
I have read that SemaphoreSlim.WaitAsync and Release methods can help.
But how do they fit in the below sample where multiple tasks need to wait on a lock object, and releasing the lock wakes up all waiting tasks ?
private bool m_condition = false;
private readonly Object m_lock = new Object();
private async Task<bool> SyncInteralWithPoolingAsync(
SyncDatabase db,
List<EntryUpdateInfo> updateList)
{
List<Task> activeTasks = new List<Task>();
int addedTasks = 0;
int removedTasks = 0;
foreach (EntryUpdateInfo entryUpdateInfo in updateList)
{
Monitor.Enter(m_lock);
//If 5 tasks are waiting in ProcessEntryAsync method
if(m_count >= 5)
{
//Do some batch processing to obtian values to set for adapterEntry.AdapterEntryId in ProcessEntryAsync
//.......
//.......
m_condition = true;
Monitor.PulseAll(m_lock); // Wakes all waiters AFTER lock is released
}
Monitor.Exit(m_lock);
removedTasks += activeTasks.RemoveAll(t => t.IsCompleted);
Task processingTask = Task.Run(
async () =>
{
await this.ProcessEntryAsync(
entryUpdateInfo,
db)
.ContinueWith(this.ProcessEntryCompleteAsync)
.ConfigureAwait(false);
});
activeTasks.Add(processingTask);
addedTasks++;
}
}
private async Task<bool> ProcessEntryAsync(SyncDatabase db, EntryUpdateInfo entryUpdateInfo)
{
SyncEntryAdapterData adapterEntry =
updateInfo.Entry.AdapterEntries.FirstOrDefault(e => e.AdapterId == this.Config.Id);
if (adapterEntry == null)
{
adapterEntry = new SyncEntryAdapterData()
{
SyncEntry = updateInfo.Entry,
AdapterId = this.Config.Id
};
updateInfo.Entry.AdapterEntries.Add(adapterEntry);
}
m_condition = false;
Monitor.Enter(m_lock);
while (!m_condition)
{
m_count++;
Monitor.Wait(m_lock);
}
m_count--;
adapterEntry.AdapterEntryId = .... //Set Value obtained form batch processing
Monitor.Exit(m_lock);
}
private void ProcessEntryCompleteAsync(Task<bool> task, object context)
{
EntryProcessingContext ctx = (EntryProcessingContext)context;
try
{
string message;
if (task.IsCanceled)
{
Logger.Warning("Processing was cancelled");
message = "The change was cancelled during processing";
}
else if (task.Exception != null)
{
Exception ex = task.Exception;
Logger.Warning("Processing failed with {0}: {1}", ex.GetType().FullName, ex.Message);
message = "An error occurred while synchronzing the changed.";
}
else
{
message = "The change was successfully synchronized";
if (task.Result)
{
//Processing
//...
//...
}
}
}
catch (Exception e)
{
Logger.Info(
"Caught an exception while completing entry processing. " + e);
}
finally
{
}
}
Thanks
I'm developing a Windows Form app with C# and .NET Framework 4.0.
I'm using Task to run a long running task and I need to update UI with some log messages every time my task process a code.
There is a Queue processing that code, I need to show that a code has been processed.
private Task taskReadCodeAuto;
private delegate void RefreshTextBox();
private Queue CodesReceived;
public MainForm()
{
InitializeComponent();
logMessages = new List<string>();
CodesReceived = new Queue();
taskReadCodeAuto = new Task(() => ProcessCodesReceived());
}
private void ProcessCodesReceived()
{
int result;
try
{
while (CodesReceived.Count > 0)
{
string code = CodesReceived.Dequeue().ToString();
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), string.Format("Sending code {0} to ReadCodeAuto...", code));
if (trzic == null)
{
result =
TRZIC.ReadCodeAuto(
ConnStringTextBox.Text,
byte.Parse(AggregationNumeric.Value.ToString()),
code);
}
else
{
result =
trzic.ReadCodeAuto(
byte.Parse(AggregationNumeric.Value.ToString()),
code);
}
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), string.Format("Code sent {0}. Result: {1}", code, result));
}
}
catch (Exception ex)
{
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), "Error: " + ex.Message);
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
finally
{
InsertProfileMessage(DateTime.Now.ToString("HH:mm:ss.fff"), "END BG-WORKER");
}
}
private void InsertProfileMessage(string time, string message)
{
string profileString =
string.Format("{0} - {1}", time, message);
logMessages.Add(profileString);
if (this.InvokeRequired)
{
RefreshTextBox d = new RefreshTextBox(RefreshTextBoxResults);
Invoke(d);
}
else
{
RefreshTextBoxResults(profileString + "\n");
}
}
private void RefreshTextBoxResults(string text)
{
LogTextBox.AppendText(text);
}
My problem is that I don't know how to pass the text to show on LogTextBox using Invoke.
How can I do it?
Use the overload of Invoke which takes an Object[] as a parameter for the arguments to be supplied to your method.
You can add the parameters after the invoke:
Action<string> d = RefreshTextBoxResults;
this.Invoke(d, profileString + "\n");
Or invoke an action where the parameter is already included (which is this case is suitable regarding re usability)
Action d= () =>RefreshTextBoxResults(profileString + "\n");
if (this.InvokeRequired)
{
Invoke(d);
}
else
{
d();
}
PS, if you want to use your RefreshTextBox delegate instead of an Action, the RefreshTextBox delegate should be altered to include a string parameter
You would have to use the overload of Invoke which uses an array:
MSDN documentation
Pass the text value like below.
RefreshTextBox d = new RefreshTextBox(RefreshTextBoxResults);
Invoke(d,new object[] {“Pass value here”});
I'm a newb in programming and I'm trying to do my first thingy that would be for someone else and not just me (so shouldn't be that crappy ^^ )
It's a Online-Checker for clients in LAN network (so he can just paste a list of clients, and it returns the online or offline).
fyi: I'm using Try/Catch because ping.send to an offline host returns in an Error which crashed the application.
Currently it looks like this:
private void btn_check_Click(object sender, EventArgs e)
{
string[] hosts = txt_hosts.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
foreach (String host in hosts)
{
pinger(host);
}
}
public void pinger(string host)
{
var ping = new System.Net.NetworkInformation.Ping();
try
{
var result = ping.Send(host);
txt_result.Text += "true" + Environment.NewLine;
Application.DoEvents();
}
catch
{
txt_result.Text += "false"+Environment.NewLine;
Application.DoEvents();
}
}
Now, the interface is like frozen whenever a ping.send is processing (and that's quiet long cause of the timeout of pings).
Is there any way to do this threaded? Before I tried to start a thread, but that doesn't work either because both write in txt_result and that returns an error.
Thanks for any help!
If use acync/await:
// send request
foreach (string host in hosts)
pinger(host);
// async function
async void pinger(string host)
{
var ping = new System.Net.NetworkInformation.Ping();
bool bResp;
try
{
var result = await ping.SendPingAsync(host, 4000);
bResp = result.Status == System.Net.NetworkInformation.IPStatus.Success;
}
catch { bResp = false; }
txt_result.Text += bResp.ToString() + Environment.NewLine;
}
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
pinger(host);
});
It could throw an exception at the line : txt_result.Text = "...";
Because you are trying to modify a value in a thread from another thread.
So you could write:
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
{
txt_result.Text = "...";
}));
Which will request the UI thread to modify the value.
Run on a background worker.
public void pinger(string host)
{
var bw = new BackgroundWorker();
bw.DoWork += delegate(object sender, DoWorkEventArgs e)
{
var ping = new System.Net.NetworkInformation.Ping();
try
{
var result = ping.Send(host);
e.Result = new object[] { result};
}
catch(Exception ex)
{
// Catch specific exceptions here as needed
}
};
bw.RunWorkerCompleted += (bw_txt_results);
bw.RunWorkerAsync();
}
private void bw_txt_results(object sender, RunWorkerCompletedEventArgs e)
{
txt_result = e.result[0].ToString();
}
I am trying to manage a thread bot situation. I want to add them in a dictionary for later access (shutdown, pause, pass variables) but I always get them as null even after the bot started (the null variable is temp_bot).
private static
Dictionary<string, Bot> BotsOnline = new Dictionary<string, Bot>();
Bot temp_bot;
new Thread(
() =>
{
int crashes = 0;
while (crashes < 1000)
{
try
{
temp_bot = new Bot(config, sMessage, ConvertStrDic(offeredItems),
ConvertStrDic(requestedItems), config.ApiKey,
(Bot bot, SteamID sid) =>
{
return (SteamBot.UserHandler)System.Activator.CreateInstance(
Type.GetType(bot.BotControlClass), new object[] { bot, sid });
},
false);
}
catch (Exception e)
{
Console.WriteLine("Error With Bot: " + e);
crashes++;
}
}
}).Start();
//wait for bot to login
while (temp_bot == null || !temp_bot.IsLoggedIn)
{
Thread.Sleep(1000);
}
//add bot to dictionary
if (temp_bot.IsLoggedIn)
{
BOTSdictionary.Add(username, temp_bot);
}
The new thread doesn't know about the temp_bot object as you need to pass it in to the lambda expression. Try:
new Thread(
(temp_bot) =>
{
int crashes = 0;
while (crashes < 1000)
{
try
{
temp_bot = new Bot(config, sMessage, ConvertStrDic(offeredItems),
ConvertStrDic(requestedItems), config.ApiKey,
(Bot bot, SteamID sid) =>
{
return (SteamBot.UserHandler)System.Activator.CreateInstance(
Type.GetType(bot.BotControlClass), new object[] { bot, sid });
},
false);
}
catch (Exception e)
{
Console.WriteLine("Error With Bot: " + e);
crashes++;
}
}
}
).Start();
I am using async and await keywords to make an app responsive
I have the following code in the View when the property is raised in the ViewModel.
VIEW: Button Click Event
private async void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
// Property Change
_viewModel.Words = await Task.Factory.StartNew(() => File.ReadAllLines("Words.txt").ToList()); // takes time to read about 3 - 4 seconds
switch (_viewModel.RadioButtonWordOrderSelection)
{
case MainWindowViewModel.RadioButtonWordOrderSelections.NormalOrder:
break;
case MainWindowViewModel.RadioButtonWordOrderSelections.ReverseOrder:
await Task.Factory.StartNew(() =>
{
var words = _viewModel.Words.ToList();
words.Reverse();
// Property Change
_viewModel.Words = words;
});
break;
case MainWindowViewModel.RadioButtonWordOrderSelections.Shuffle:
await Task.Factory.StartNew(() =>
{
// Property Change
_viewModel.Words = _viewModel.Words.Shuffle().ToList();
});
break;
}
await Task.Factory.StartNew(() => DownloadSomething(_viewModel.Words)); // takes time to read about 30 - 40 seconds
button1.IsEnabled = true;
}
_viewModel.Progress gets updated in the View
VIEW: Private Method Takes 30 - 40 seconds To Complete
private void DownloadSomething(IEnumerable<string> words)
{
// Property Change
_viewModel.Progress = 0;
foreach (var word in words)
{
// Property Change
_viewModel.Word = word;
try
{
// Some code WebClient download code here
}
catch (Exception e)
{
//Trace.WriteLine(e.Message);
}
// Property Change
_viewModel.Progress++;
}
}
VIEW: Property Change Event Handled
void _viewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
try
{
switch(e.PropertyName)
{
case "Progress":
// Since its a different Thread
// http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
// Sets the Value on a ProgressBar Control.
// This will work as its using the dispatcher
// The following works
//Dispatcher.Invoke(
// DispatcherPriority.Normal,
// new Action<double>(SetProgressValue),
// _viewModel.Progress);
// from http://stackoverflow.com/questions/2982498/wpf-dispatcher-the-calling-thread-cannot-access-this-object-because-a-differen
progress1.Dispatcher.Invoke(
DispatcherPriority.Normal,
new Action(() =>
{
progress1.Value = _viewModel.Progress;
})
);
// This will throw an exception
// (it's on the wrong thread)
//progress1.Value = _viewModel.Progress;
break;
case "Words":
// Since its a different Thread
// http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
// Sets the Max Value on a ProgressBar Control.
// This will work as its using the dispatcher
// The following Works
//Dispatcher.Invoke(
// DispatcherPriority.Normal,
// new Action<double>(SetProgressMaxValue),
// _viewModel.Words.Count);
// from http://stackoverflow.com/questions/2982498/wpf-dispatcher-the-calling-thread-cannot-access-this-object-because-a-differen
progress1.Dispatcher.Invoke(
DispatcherPriority.Normal,
new Action(() =>
{
progress1.Maximum = _viewModel.Words.Count;
})
);
// This will throw an exception
// (it's on the wrong thread)
//progress1.Maximum = _viewModel.Words.Count;
break;
case "Word":
// Since its a different Thread
// http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
// Sets the Contant on a Label Control.
// This will work as its using the dispatcher
// The following Works
//Dispatcher.Invoke(
// DispatcherPriority.Normal,
// new Action<string>(SetLastWordValue),
// _viewModel.Word);
// from http://stackoverflow.com/questions/2982498/wpf-dispatcher-the-calling-thread-cannot-access-this-object-because-a-differen
labelLastWord.Dispatcher.Invoke(
DispatcherPriority.Normal,
new Action(() =>
{
labelLastWord.Content = _viewModel.Word;
})
);
// This will throw an exception
// (it's on the wrong thread)
//labelLastWord.Content = _viewModel.Word;
break;
case "RadioButtonWordOrderSelection":
break;
default:
throw new NotImplementedException("[Not implemented for 'Property Name': " + e.PropertyName + "]");
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message + "\n" + ex.StackTrace);
}
}
The UI updates perfectly for the progressBar1 and labelLastWord! I am facing a problem though, when progressBar1 and labelLastWord is updating the rest of the UI is frozen.
Is there a work around to fix this?
I greatly appreciate any help!
I strongly recommend you follow the guidelines in the Task-based Async Programming document. This is far better than just shunting work off to the thread pool via StartNew. If you do have CPU-bound operations, you could use Task.Run, but for everything else, use the existing async-ready endpoints.
Also, I find it useful to treat the entire VM as being in the UI context. So PropertyChanged is always raised in a UI context. Data binding in particular depends on this.
private async Task<List<string>> ReadAllLinesAsync(string file)
{
var ret = new List<string>();
using (var reader = new StreamReader(file))
{
string str = await reader.ReadLineAsync();
while (str != null)
{
ret.Add(str);
str = await reader.ReadLineAsync();
}
}
return ret;
}
private async void button1_Click(object sender, RoutedEventArgs e)
{
button1.IsEnabled = false;
_viewModel.Words = await ReadAllLinesAsync("Words.txt");
List<string> words;
switch (_viewModel.RadioButtonWordOrderSelection)
{
case MainWindowViewModel.RadioButtonWordOrderSelections.NormalOrder:
break;
case MainWindowViewModel.RadioButtonWordOrderSelections.ReverseOrder:
await Task.Run(() =>
{
words = _viewModel.Words.ToList();
words.Reverse();
});
_viewModel.Words = words;
break;
case MainWindowViewModel.RadioButtonWordOrderSelections.Shuffle:
await Task.Run(() =>
{
words = _viewModel.Words.Shuffle().ToList();
});
_viewModel.Words = words;
break;
}
await DownloadSomething(_viewModel.Words);
button1.IsEnabled = true;
}
private async Task DownloadSomething(IEnumerable<string> words)
{
_viewModel.Progress = 0;
foreach (var word in words)
{
_viewModel.Word = word;
try
{
await ...; // async WebClient/HttpClient code here
}
catch (Exception e)
{
//Trace.WriteLine(e.Message);
}
_viewModel.Progress++;
}
}
void _viewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
try
{
switch(e.PropertyName)
{
case "Progress":
progress1.Value = _viewModel.Progress;
break;
case "Words":
progress1.Maximum = _viewModel.Words.Count;
break;
case "Word":
labelLastWord.Content = _viewModel.Word;
break;
case "RadioButtonWordOrderSelection":
break;
default:
throw new NotImplementedException("[Not implemented for 'Property Name': " + e.PropertyName + "]");
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message + "\n" + ex.StackTrace);
}
}
As a closing note, I recommend you purchase and read thoroughly Josh Smith's MVVM book. You're using terms like "View" and "ViewModel", but the way you're using these components, you're completely avoiding all the advantages of the MVVM pattern.