How to abort specific thread in task parallel library with C# - c#

I have this tricky task I've been trying to achieve for quiet sometime but till now I couldn't think of anything to make it work. anyway here is the scenario...
I have a winform application contains a listview and a button.
the listview contains 1 column which holds the data I need to pass to my functions later on. the column contains lets say 50 rows containing a list of links.
Now I have this function which I'm using to fetch and grab the contents of these links (5 links at a time) with parallel multithreaded mode using (Task Parallel Library):
//List<int> currentWorkingItem //contains the indices of the items in listview
//List<string> URLsList //contains the URLs of the items in listview
Parallel.ForEach(URLsList, new ParallelOptions() { MaxDegreeOfParallelism = 5 }, (url, i, j) =>
{
//show to user this link is currently being downloaded by highlighting the item to green...
this.BeginInvoke((Action)(delegate()
{
//current working item
mylistview.Items[currentWorkingItem[(int)j]].BackColor = green;
}));
//here I download the contents of every link in the list...
string HtmlResponse = GetPageResponse(url);
//do further processing....
});
Now the above code works perfectly... but sometimes I want the user to abort certain thread which is currently running and continue with the rest of the threads in the list... is that achievable in this? if so please help me out.. I'd really appreciate any solution or suggestions..

Try using Task library with cancellation tokens. I find it more elegant and safer approach to do your thing. Here is a quote good example of doing that:
using System;
using System.Threading.Tasks;
using System.Threading;
namespace CancelTask
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Press 1 to cancel task");
var cTokenSource = new CancellationTokenSource();
// Create a cancellation token from CancellationTokenSource
var cToken = cTokenSource.Token;
// Create a task and pass the cancellation token
var t1 = Task<int>.Factory.StartNew(()
=> GenerateNumbers(cToken), cToken);
// to register a delegate for a callback when a
// cancellation request is made
cToken.Register(() => cancelNotification());
// If user presses 1, request cancellation.
if (Console.ReadKey().KeyChar == '1')
{
// cancelling task
cTokenSource.Cancel();
}
Console.ReadLine();
}
static int GenerateNumbers(CancellationToken ct)
{
int i;
for (i = 0; i < 10; i++)
{
Console.WriteLine("Method1 - Number: {0}", i);
Thread.Sleep(1000);
// poll the IsCancellationRequested property
// to check if cancellation was requested
if (ct.IsCancellationRequested)
{
break;
}
}
return i;
}
// Notify when task is cancelled
static void cancelNotification()
{
Console.WriteLine("Cancellation request made!!");
}
}
}
Original article could be found here: http://www.dotnetcurry.com/ShowArticle.aspx?ID=493

ok after struggling with this I finally found an efficient and an easy solution for this..
it required me only a hashtable which contains the indicies of the selected items in the listview and a simple bool value. the index is the key and the bool (true, false) is the value. the bool value is like an (on/off) switch indicates that the current loop is aborted or not.. so in order to abort specific thread simple I need to pass the key(the index) of the selected item on my listview to the foreach loop and check if the bool switch is on or off and that's basically it...
so my final code will be like this:
//I declared the hashtable outside the function so I can manage it from different source.
private Hashtable abortingItem;
Now when I click grab button it should fill the hashtable with the selected indicies...
abortingItem = new Hashtable();
for (int i = 0; i < myURLslist.SelectedItems.Count(); i++)
{
//false means don't abort this.. let it run
abortingItem.Add(myURLslist.SelectedItems[i].index, false);
}
//here should be the code of my thread to run the process of grabbing the URLs (the foreach loop)
//..........................
now if I need to abort specific item all I need is to select the item in the listview and click abort button
private void abort_Click(object sender, EventArgs e)
{
if (abortingItem != null)
{
for (int u = 0; u < myURLslist.SelectedIndices.Count; u++)
{
//true means abort this item
abortingItem[myURLslist.SelectedIndices[u]] = true;
}
}
}
In my foreach loop all I need is a simple if else statement to check if the bool is on or off:
//List<int> currentWorkingItem //contains the indices of the items in listview
//List<string> URLsList //contains the URLs of the items in listview
Parallel.ForEach(URLsList, new ParallelOptions() { MaxDegreeOfParallelism = 5 }, (url, i, j) =>
{
//aborting
if (!(bool)abortingItem[currentWorkingItem[(int)j]])
{
//show to user this link is currently being downloaded by highlighting the item to green...
this.BeginInvoke((Action)(delegate()
{
//current working item
mylistview.Items[currentWorkingItem[(int)j]].BackColor = green;
}));
//here I download the contents of every link in the list...
string HtmlResponse = GetPageResponse(url);
//do further processing....
}
else
{
//aborted
}
});
that's simply it..

Related

Pause a for loop until external callback method is called

Non-technical explanation of what I'm trying to do:
I have a bot that sends a user a picture, when the user clicks the "done" button it sends him the next picture, and so on until there are no more pictures.
Problem:
The only way to listen to that button using the API I'm using is by using this method:
private static async void BotOnCallbackQueryReceived(object sender, CallbackQueryEventArgs callbackQueryEventArgs) {
var callbackQuery = callbackQueryEventArgs.CallbackQuery;
try {
await Task.Run(() => {
Bot.AnswerCallbackQueryAsync(callbackQuery.Id, "Answer has been submitted");
string data = callbackQuery.Data.ToLower();
if (data.Equals("notfound")){
//TODO He couldn't find the item
}
else if (data.Equals("done")) {
}
});
}
}
And of course, subscribe to it via:
Bot.OnCallbackQuery += BotOnCallbackQueryReceived;
To send the image and menu, I have a for loop that covers a List<Item> (Item is a POCO).
await Task.Run(() => {
// - Which buttons to send. MenuItem's constructor is (ButtonText, Callbackdata)
List<MenuItem> menuItems = new List<MenuItem>();
menuItems.Add(new MenuItem("Done", "done"));
menuItems.Add(new MenuItem("Not found", "notFound"));
//telegramLineItemObjects is a List<Item>
foreach (Item item in telegramLineItemObjects) {
string message = $"<b>Shelf Number:</b> {item.ShelfNumber}\n<b>Sku:</b> {item.Sku}\n<b>Barcode:</b> {item.Barcode}\n<b>Description:</b> {item.Description}\n<b>Quantity:</b> {item.Quantity}\n";
//sendImage(Image URL, message, message parse mode)
telegram.sendImage(item.Picture, message, ParseMode.HTML);
Message sentMenu = telegram.sendMenu(message, menuItems);
}
}
I want to pause the for loop, wait for BotOnCallBackQueryReceived to be called, then resume the for loop.
To do that, I need a way to listen for when BotOnCallBackQueryReceived is called. How do I do that?

C# Accessing form control from different thread

I've been looking at https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls and other Stack Overflow questions for a good long while now to figure out how to use thread-safe methods to access a ListView controls from different threads.
Here's how I want to implement parallel tasks in my program:
I call four different methods in parallel with:
Parallel.Invoke(ProcessLow, ProcessMed, ProcessHigh, ProcessSprint);
Each method searches through the same collection (data[i].Knots) with a for loop and looks for a different range of values within that collection, then if one of the methods finds an appropriate value within the range it's looking for it adds the time and knots (data[i].Time, data[i].Knots) to its respective ListView (the methods write to lstvLow, lstvMed, lstvHigh and lstvSprint respectively). At the moment it's just throwing the exception for non-thread safe code. Also will it break if I have different threads just reading off the same collection? (if so, how can I work around this?)
tldr: Parallel processing is new to me, how do I make a thread-safe call to a windows form control.
And also if you can, point me in the direction of some good reading other than msdn for Parallel tasking.
edit: this is with winforms
To make thread safe call. use Control.Invoke(); method. Assuming you have instance of ListView mylistView; you can write:
object result = mylistView.Invoke(new Action(() => mylistView.DoSomething()));
As I understand, you are new to multitasking. I hope my case study will help you. Create a windows form with next controls:
startButton, stopButton as Button
minTextEdit, maxTextEdit as TextEdit
listListView as ListView
For startButton and stopButton use the appropriate methods: startButton_Click and stopButton_Click.
Full the code below:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Thread0
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private static readonly Random _random = new Random();
private List<int> lst = new List<int>();
private bool isRun = false;
private void startButton_Click(object sender, EventArgs e)
{
isRun = true;
stopButton.Enabled = true;
startButton.Enabled = false;
var tskMain = Task.Run(() =>
{
for (int i = 0; i < 8; i++)
{
var tsk1 = Task.Run(() =>
{
while (true)
{
int max = 0;
int min = Int32.MaxValue;
lock (lst)
{
int num = _random.Next(1, 1000000);
lst.Add(num);
foreach (var x in lst)
{
if (x > max) max = x;
if (min > x) min = x;
}
listListView.BeginInvoke(new Action(() => listListView.Items.Insert(0, num.ToString())));
}
maxTextBox.BeginInvoke(new Action(() => maxTextBox.Text = max.ToString()));
minTextBox.BeginInvoke(new Action(() => minTextBox.Text = min.ToString()));
if (!isRun) break;
Thread.Sleep(100);
}
});
}
});
}
private void stopButton_Click(object sender, EventArgs e)
{
isRun = false;
stopButton.Enabled = false;
startButton.Enabled = true;
}
}
}
When you click on the Start button, the stream create and run tskMain thread, which creates 8 more threads. In each of them, an integer is added to the list of values lst, and the search for the maximum and minimum values. The new number is also added to the listListView, and the maximum and minimum value in the corresponding minTextEdit and maxTextEdit.
For work it is convenient to use lambda expression.
lock(lst) ... blocks work with the list of values. One action per time, so that there are no exceptions.
And the BeginInvoke method allows you to call methods from threads for form elements that are in the main form stream. The Action is used to "transform" the lambda expression into a delegate method.
Inside each thread, the variable IsRun is checked, if its value becomes false, the thread execution stops.
Well, to ensure that everything is not very quickly use .Sleep

"Branching" out progress across different processes (loops)

Objective
I'm trying to build a progress tracking mechanism which supports "branching".
Abstract
The principal concept here is that at every stage I know the current index and count of steps till this stage is complete.
Sample Scenario
var tasks = new Task[]
{
new Task(5), // 5 sub tasks
new Task(2), // 2 sub tasks
};
Assuming these items are run serially, progress percentage would look as follows:
0%
10%
20%
30%
40%
50%
50%
75%
100%
100%
Question Time!
First of all, what are the correct terms for what I'm doing? I have been unable to properly search because of this.
Secondly, what are the mathematics of what I'm doing? What are the variables that I have to track?
Final Product
I would like to end up with an interface that looks like this:
class TaskManagerExample
{
void Execute(Task[] tasks)
{
var pm = new ProgressManager(); // <- this class does the magic
// v-- and this is how the magic is used :)
pm.ProgressChanged += (object sender, EventArgs e) =>
{
progressBar1.Value = (sender as ProgressManager).TotalProgress;
};
pm.SetTotal(tasks.Length);
foreach (var task in tasks)
{
task.Execute(pm.Branch());
pm.NextStep();
}
}
}
class Task
{
protected int SubTasks = 0;
public Task(int subTasks){ this.SubTasks = subTasks; }
public void Execute(ProgressManager pmb)
{
pmb.SetTotal(this.SubTasks);
for(int i = 0; i < this.SubTasks; i++)
{
// do something
pmb.NextStep();
}
}
}
My problem is the implementation for ProgressManager. I've been able to achieve a parent-child relation so Branch() and NextStep() are ok. I'm mostly confused about how TotalProgress should be implemented.
How would one go through the tree to calculate the right progress position?

VS2010, C#, passing parameter bulk into threads, speed issue

I have the code below where it update the data within listview, I was trying to pass parameter into main code which update the listview table.
I have external code that can send burst data stream and it shown same value on the list.
Below is based on anonymous method which work but earlier data get overwritten.
The listview is too slow which slow down main program (not listed here) the burst data goes to this code and use separate thread to handle the display (about 20 set). The burst data is about 20 set of dataTX array, etc.
I'm open for suggestion how to fix this.
==========================================================================
public void LIN_Request_Add_Message(bool isCRCIncluded) // This Add new line for request based message.
{
byte[] dataTX = new byte[10];
dataTX = myLinTools.LinArrayTXArray();
DateTime d = DateTime.Now;
this.ReqAddMessageThread = new Thread(delegate() { ReqAddMessageThreadProc(isCRCIncluded, dataTX, d); }); //anonymous method
this.ReqAddMessageThread.Start();
}
#endregion
private void ReqAddMessageThreadProc(bool isCRCIncluded, byte[] dataTX, DateTime d)
{
if (this.OutputView.InvokeRequired)
{
test1Callback del = new test1Callback(ReqAddMessageThreadProc);
this.Invoke(del, new object[] { isCRCIncluded, dataTX,d });
return;
}
if (this.Visible == true)
{
SendMessage(this.Handle, WM_SETREDRAW, false, 0);
}
int length = myLinTools.LINDataLength;
int pCRC = 0;
elem = new ListViewItem(m_Item.ToString());
elem.SubItems.Add(d.Date.ToShortDateString());
elem.SubItems.Add(d.ToShortTimeString() + ":" + d.Second.ToString());
elem.SubItems.Add("");
for (int i = 0; i < length + 1; i++)
{
elem.SubItems.Add(dataTX[i].ToString("X2"));
pCRC = i;
}
for (int i = length; i < 8; i++)
{
elem.SubItems.Add(" "); // fill gaps
}
if (isCRCIncluded == true) // Does the message contains processed CRC data?
{
elem.SubItems.Add(dataTX[pCRC + 1].ToString("X2"));
}
else // No, then make one for display only!!
{
Byte CRC = myLinTools.CRC_Processor(false);
elem.SubItems.Add(CRC.ToString("X2"));
}
this.OutputView.Items.Add(elem);
this.OutputView.EnsureVisible(m_Item);
if (myLinTools.IsRequestResponse == true) // Request Message Only
{
if (this.Visible == true) // Is form open?
{
SendMessage(this.Handle, WM_SETREDRAW, true, 0);
this.Refresh();
}
}
m_Item++;
}
=================================================================================
Thanks KazR, I modified the code which worked fine, however it slow down the other high level program (call it main program), that making data transfer to this program that display data. One of the requirement that the main program stream the data without delay or pause cause by listview in this display program. That why I'm looking for way to use thread, so it release the control back to main program and thus operates faster, but however there is issue in keep data from being over-written by next thread, since listview is slow. Perhaps I should consider a buffer, which update only when there is no activity in main program.
I do not wish to use virtual, I'm open for alternative suggestion.
==================================================================================
delegate void ReqAddMessageTCallback(bool isCRCIncluded, byte[] dataTX, DateTime d);
#region//==================================================LIN_Request_Add_Message
public void LIN_Request_Add_Message(bool isCRCIncluded) // This Add new line for request based message.
{
byte[] dataTX = new byte[10];
dataTX = myLinTools.LinArrayTXArray();
DateTime d = DateTime.Now;
ReqAddMessageThreadProc(isCRCIncluded, dataTX, d);
}
#endregion
#region//==================================================ReqAddMessageThreadProc
private void ReqAddMessageThreadProc(bool isCRCIncluded, byte[] dataTX, DateTime d)
{
if (this.OutputView.InvokeRequired)
{
ReqAddMessageTCallback del = new ReqAddMessageTCallback(ReqAddMessageThreadProc);
this.BeginInvoke(del, new object[] { isCRCIncluded, dataTX, d });
return;
}
if (this.Visible == true)
{
SendMessage(this.Handle, WM_SETREDRAW, false, 0);
}
int length = myLinTools.LINDataLength;
int pCRC = 0;
elem = new ListViewItem(m_Item.ToString());
From your code example it appears that you're creating a new Thread object each time you receive data and all this thread is doing is calling the ReqAddMessageThreadProc method. Assuming that calls to LIN_Request_Add_Message are not being made in the main UI thread, you could try removing the Thread creation & start calls, replace them with a direct call the ReqAddMessageThreadProc and use BeginInvoke rather than Invoke.
e.g.
public void LIN_Request_Add_Message(bool isCRCIncluded) // This Add new line for request based message.
{
byte[] dataTX = new byte[10];
dataTX = myLinTools.LinArrayTXArray();
DateTime d = DateTime.Now;
ReqAddMessageThreadProc(isCRCIncluded, dataTX, d);
}
#endregion
private void ReqAddMessageThreadProc(bool isCRCIncluded, byte[] dataTX, DateTime d)
{
if (this.OutputView.InvokeRequired)
{
test1Callback del = new test1Callback(ReqAddMessageThreadProc);
this.BeginInvoke(del, new object[] { isCRCIncluded, dataTX,d });
return;
}
etc...
The BeginInvoke call is the async version of Invoke, this should negate the need to use separate new Thread objects each time you receive new data.
You should make your ListView virtual. Then you can operate at full speed with your threads and the listview will display only the items which are currently visible. If you do append things to the ListView and not force to make it visible every time something is added you can let the user decide when he does want to scroll down. He does see when things are appended because the scrollbar is becoming smaller and smaller while items are added.
This way you can display millions of entries without any issues and you will get rid of ivoking, redrawing, refreshing the listview via SendMessage. Here is a virtual list view sample how you can change it.
By doing so your code will become simpler and faster because you do not mix background data processing with UI stuff. E.g. sorting can be done at raw data array level without doing anything in the UI except to trigger it.

Silverlight C# - How to hold loop progress until an event completes?

I'm trying to loop through the text in a textbox by word in order to spellcheck it. I've split the contents of the textbox into an array, and loop through each word in the array and run it through the spellchecker. When a misspelling is found, I have a popup with a listbox inside it display so that you can choose the correction.
The issue that I'm having, is that it just loops through the whole array and only ends up showing the last correction that needs to be done.
How do I pause the loop so that it waits for a selection to be made and then resume?
Here's the code for the loop:
foreach(string checkedWord in articleWords)
{
bool success = _spellChecker.CheckWord(checkedWord);
List<string> suggest;
if (!success)
{
suggest = _spellChecker.GetSuggestions(checkedWord);
SpellChecklistBox.Items.Clear();
foreach (string s in suggest)
{
SpellChecklistBox.Items.Add(new ListBoxItem() { Content = s });
}
SpellCheckerPopup.IsOpen = true;
SpellChecklistBox.Items.Add(new ListBoxItem() { Content = " ----------------------" });
SpellChecklistBox.Items.Add(new ListBoxItem() { Content = "Ignore" });
}
}
When the SpellCheckerPopup displays, I have an event trigger in the listbox on SelectionChange.
Basically, I need to pause the loop somehow, and then when the SelectionChange event does it's thing, have the loop resume.
Thanks in advance!
-Sootah
If I'm not misunderstanding, currently you are going to:
(1) Check each word in the loop
(2) Pause the loop when an error is found and pop up a suggestion window
(3) User select a suggestion word and resume the loop
I think it's better and easier if the solution is:
(1) Check the word from the first one
(2) Quit the check method with an error flag, and store the position in a variable, pop up a suggestion window
(3) User selects a suggestion word and when User has confirmed the suggestion(e.g. pressing OK on the suggestion window), start the CheckWordMethod again from the stored position
(4) Until step (2) quits with no error flag, which means all words are correct now (but make sure in the whole progress, users can only modify the words by your suggestion window)
#The Smartest: Your answer lead me in the correct direction; actually ended up learning a new datatype out of it! Never used a Queue before. (Which made it a HELL of a lot simpler than having to track where in the array I was at, as I first figured I thought I'd have to.. :)
Anyway, I'll accept your answer, but here's the code I ended up doing: (The actual replacing of the word in the textbox I've not implemented yet.)
private void btnSpelling_Click(object sender, RoutedEventArgs e)
{
SpellChecklistBox.Items.Clear();
string[] articleWordsArray = txtArticle.Text.Split(' ');
foreach (string word in articleWordsArray)
{
articleWords.Enqueue(word);
}
CorrectWord();
}
private void SpellChecklistBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SpellCheckerPopup.IsOpen = false;
}
private void SpellCheckerPopup_Closed(object sender, EventArgs e)
{
CorrectWord();
SpellChecklistBox.Items.Clear();
}
Queue<string> articleWords = new Queue<string>();
private void CorrectWord()
{
if (articleWords.Count() > 0)
{
string checkedWord = articleWords.Dequeue();
bool success = _spellChecker.CheckWord(checkedWord);
List<string> suggest;
if (!success)
{
suggest = _spellChecker.GetSuggestions(checkedWord);
foreach (string s in suggest)
{
SpellChecklistBox.Items.Add(new ListBoxItem() { Content = s });
}
SpellCheckerPopup.IsOpen = true;
SpellChecklistBox.Items.Add(new ListBoxItem() { Content = " ----------------------" });
SpellChecklistBox.Items.Add(new ListBoxItem() { Content = "Ignore" });
SpellCheckerPopup.IsOpen = true;
}
}
}
It's all pretty straight forward courtesy of the Queue datatype. When the spelling button gets clicked it loads the TextBox into an array, and then I loop through the array to enqueue the items into the articleWords queue, after which it calls CorrectWord(). CorrectWord() then loads the relevant list after dequeueing from articleWords, and on the PopUp. Closed event it clears the ListBox and calls CorrectWord() which will keep bringing back the PopUp until there are no more words to be corrected. :)

Categories

Resources