Accessing Object from different thread - Tasks - c#

i am new to tasks and don't really know if its good to use it the way i want.
I have a Class which inherits from TextBlock and colorize the text (Syntaxhighlighting).
It does take awhile so i want to do it in Background
Here is my code so far:
async void SyntaxTextBlock_TargetUpdated(object sender, System.Windows.Data.DataTransferEventArgs e)
{
if ((clientSettings.SyntaxCheck || clientSettings.SyntaxHighlighting) && !string.IsNullOrEmpty(Text))
{
string value = Text;
Syntax syntax = await Task.Factory.StartNew(() => DoSyntax(value));
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
if (syntax.IsTextDecoration)
TextDecorations.Add(Syntax.TextDecoration);
ToolTip = string.Join(Environment.NewLine, syntax.Messages);
Text = null;
foreach (Run run in syntax.Runs)
Inlines.Add(run);
}));
}
}
My problem is i cant access the syntax.Runs List from the object Syntax which is returned by DoSyntax(value).
Error: the calling thread cannot access this object because a different thread owns it
I tried it with the scheduler TaskScheduler.FromCurrentSynchronizationContext and it worked, but the GUI freezed.
Is there a way to do this on multiple different threads while the gui does not freeze?

The problem is that DoSyntax is creating thread-affine objects (specifically, Syntax) while on a background thread.
You'll need to change DoSyntax so that it will only create "normal" objects (like a list of ranges with colors/semantics) on the background thread, and then have your UI thread process that and create the actual Syntax types, if necessary.

Related

How to invoke multiple controls in a single thread?

I am fetching data from a database and binding it to 3 different combo boxes simultaneously based on the parent comboBox value. Following is the example, I have three combo boxes named
i. comboBoxBranch
ii. comboBoxClass
iii. comboBoxSection
Value of comboBoxClass is fetched from database on the basis of selected branch, Similarly the value of comboBoxSection is fetched on the basis of selected branch and selected class. So the order of binding is (ComboBoxBranch) then comboBoxClass and then comboBoxSection.
Now in order to acheive this I am using seperate thread to call GetBranches() method to bind data with comboboxBranch in following way.
private void GetBranches() (This is working perfectly fine)
{
if (comboBoxBranches.InvokeRequired)
{
comboBoxBranches.BeginInvoke(((MethodInvoker) delegate
{
comboBoxBranches.DataSource = _schoolManagementSystemServiceClient.GetBranches();
comboBoxBranches.ValueMember = "BranchId";
comboBoxBranches.DisplayMember = "BranchName";
}));
}
Now the problem occurs how should I bind data with other two comboBoxes that are comboxClass and comboBoxSection, Should I use another thread to as I am using for Getbranch Method or there is any other clean method to achieve this. Following is my GetClasses method that I am calling in comboBoxBranches_SelectedValueChanged() event method of comboBoxBranches.
private void comboBoxBranches_SelectedValueChanged(object sender, EventArgs e)
{
Thread thread=new Thread(GetClasses());
thread.start();
}
private void GetClasses()// in this method how should I achieve invoking for multiple controls? What should I do here?
{
if (InvokeRequired)
{
comboBoxBranches.BeginInvoke(((MethodInvoker) delegate
{
Branch branch = comboBoxBranches.SelectedItem as Branch;
}));
comboBoxClasses.BeginInvoke((MethodInvoker) delegate
{
comboBoxClasses.DataSource = _schoolManagementSystemServiceClient.GetClasses(branch.BranchId);
comboBoxClasses.ValueMember = "ClassId";
comboBoxClasses.DisplayMember = "ClassName";
});
}
}
Same method is for comboxBoxSections whose value is based on both ComboBoxBranches and comboBoxClasses? I am new to multi-threading.
Invoking means waiting until the UI thread is idle, then switch to the UI thread and perform some actions. Therefore, the long running task (e.g. querying data from a database) must be performed before invoking takes place.
Today the preferred way to achieve this is to use async/await.
private async void comboBoxBranches_SelectedValueChanged(object sender, EventArgs e)
{
// We are in the UI thread and can access the controls directly.
Branch branch = comboBoxBranches.SelectedItem as Branch;
var classes = await Task.Run(
// This runs in a new thread. At this point the UI is not blocked.
() => _schoolManagementSystemServiceClient.GetClasses(branch.BranchId)
);
// Here the thread joins the UI thread and returns the classes.
// We are in the UI thread again. No need for Invoke.
comboBoxClasses.DataSource = classes;
comboBoxClasses.ValueMember = "ClassId";
comboBoxClasses.DisplayMember = "ClassName";
}
Note the keyword async in the method header. It tells C# to handle this method in a special way. Behind the scenes C# rewrites this method completely to make the magic happen and hides the complexity involved.
To understand how this works, you can imagine that C# puts the lines after the awaited task (the 3 lines with comboBoxClasses) into a callback method.
As explained in Async in depth (Microsoft) you also should rewrite GetClasses to work asynchronously and to return a Task<T> object, instead of starting a new thread here.
var classes = await _schoolManagementSystemServiceClient.GetClassesAsync(branch.BranchId);
See: Asynchronous programming (Microsoft).

Change label text and start threads in the same void

I have a big problem and hope you guys can help me...
I need to start a Thread called ListenOnSocket, no problem so far..
Thread ListenOnSocket = new Thread(newThreadStart(Form1.ListenOnSocket));
ListenOnSocket.Start();
But when I want to change the label from within ListenOnSocket, I get an object reference is required for the non-static field, method or property.
So normally you would passe the label on by doing this
public static void ListenOnSocket(Label lblstatus)
{
//i want to change the label from here.
lblstatus.text = "Hello";
}
but then I get
No overload for ListenOnSocket matches delegate System.Threading.Threadstart
in my threadstart.
Can anyone please help, I am really stuck, sorry if there is not much to go on I am quite new to C#.
You can use Lambda Expression to pass parameter.
Thread ListenOnSocket = new Thread( () => { Form1.ListenOnSocket(yourParameter); } );
ListenOnSocket.Start();
But you will get the CrossThreadException when the ListenOnSocket method execute. So you need to use BeginInvoke to set label text.
So search the CrossThreadException and why you will get it.
Note: I do not write the sample code for this, because searching is more beneficial.
You need to marshal this back to the UI thread:
public static void ListenOnSocket(Label lblstatus)
{
this.BeginInvoke(new Action(() =>
{
//i want to change the label from here.
lblstatus.text = "Hello";
});
}
It looks like you might actually want a ParameterizedThreadStart here.
You would pass the Label in as its parameter.
Control changes also need to be performed on the UI thread.
public void DoSomething()
{
// Actually a ParameterizedThreadStart, but you don't need to state this explicitly
//var listenOnSocket = new Thread(new ParameterizedThreadStart(ListenOnSocket));
var listenOnSocket = new Thread(ListenOnSocket);
// Pass the label as the ParameterizedThreadStart parameter
// TestLabel is a label within the form
listenOnSocket.Start(TestLabel);
}
private void ListenOnSocket(object statusLabelObject) // Parameter must be of type Object
{
var statusLabel = statusLabelObject as Label;
if (statusLabel == null)
throw new ArgumentException(#"Parameter must be of type Label", "statusLabelObject");
// Changes to controls must be performed on the UI thread.
BeginInvoke((Action)(() => statusLabel.Text = #"text"));
}
They key gotcha here is it's not valid to update a UI element (like a label) from a background thread.
If you have a long running task then you probably don't want to run that on the UI thread as it will block.
Assuming that you're creating a thread because you have something that runs for too long to run on the UI thread, it might be worth looking into way of marshalling calls from background threads onto the UI thread.
For more information on how to do this see How to update the GUI from another thread in C#? if you're looking to update the status from a long running task, you might want to look into background worker: MSDN: How to Use Background Worker which is a helper class designed to help with long running background tasks.

Parallel.ForEach freezing on last loop [duplicate]

More newbie questions:
This code grabs a number of proxies from the list in the main window (I couldn't figure out how to make variables be available between different functions) and does a check on each one (simple httpwebrequest) and then adds them to a list called finishedProxies.
For some reason when I press the start button, the whole program hangs up. I was under the impression that Parallel creates separate threads for each action leaving the UI thread alone so that it's responsive?
private void start_Click(object sender, RoutedEventArgs e)
{
// Populate a list of proxies
List<string> proxies = new List<string>();
List<string> finishedProxies = new List<string>();
foreach (string proxy in proxiesList.Items)
{
proxies.Add(proxy);
}
Parallel.ForEach<string>(proxies, (i) =>
{
string checkResult;
checkResult = checkProxy(i);
finishedProxies.Add(checkResult);
// update ui
/*
status.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
status.Content = "hello" + checkResult;
}
)); */
// update ui finished
//Console.WriteLine("[{0}] F({1}) = {2}", Thread.CurrentThread.Name, i, CalculateFibonacciNumber(i));
});
}
I've tried using the code that's commented out to make changes to the UI inside the Parallel.Foreach and it makes the program freeze after the start button is pressed. It's worked for me before but I used Thread class.
How can I update the UI from inside the Parallel.Foreach and how do I make Parallel.Foreach work so that it doesn't make the UI freeze up while it's working?
Here's the whole code.
You must not start the parallel processing in your UI thread. See the example under the "Avoid Executing Parallel Loops on the UI Thread" header in this page.
Update: Or, you can simply create a new thread manuall and start the processing inside that as I see you have done. There's nothing wrong with that too.
Also, as Jim Mischel points out, you are accessing the lists from multiple threads at the same time, so there are race conditions there. Either substitute ConcurrentBag for List, or wrap the lists inside a lock statement each time you access them.
A good way to circumvent the problems of not being able to write to the UI thread when using Parallel statements is to use the Task Factory and delegates, see the following code, I used this to iterate over a series of files in a directory, and process them in a Parallel.ForEach loop, after each file is processed the UI thread is signaled and updated:
var files = GetFiles(directoryToScan);
tokenSource = new CancellationTokenSource();
CancellationToken ct = tokenSource.Token;
Task task = Task.Factory.StartNew(delegate
{
// Were we already canceled?
ct.ThrowIfCancellationRequested();
Parallel.ForEach(files, currentFile =>
{
// Poll on this property if you have to do
// other cleanup before throwing.
if (ct.IsCancellationRequested)
{
// Clean up here, then...
ct.ThrowIfCancellationRequested();
}
ProcessFile(directoryToScan, currentFile, directoryToOutput);
// Update calling thread's UI
BeginInvoke((Action)(() =>
{
WriteProgress(currentFile);
}));
});
}, tokenSource.Token); // Pass same token to StartNew.
task.ContinueWith((t) =>
BeginInvoke((Action)(() =>
{
SignalCompletion(sw);
}))
);
And the methods that do the actual UI changes:
void WriteProgress(string fileName)
{
progressBar.Visible = true;
lblResizeProgressAmount.Visible = true;
lblResizeProgress.Visible = true;
progressBar.Value += 1;
Interlocked.Increment(ref counter);
lblResizeProgressAmount.Text = counter.ToString();
ListViewItem lvi = new ListViewItem(fileName);
listView1.Items.Add(lvi);
listView1.FullRowSelect = true;
}
private void SignalCompletion(Stopwatch sw)
{
sw.Stop();
if (tokenSource.IsCancellationRequested)
{
InitializeFields();
lblFinished.Visible = true;
lblFinished.Text = String.Format("Processing was cancelled after {0}", sw.Elapsed.ToString());
}
else
{
lblFinished.Visible = true;
if (counter > 0)
{
lblFinished.Text = String.Format("Resized {0} images in {1}", counter, sw.Elapsed.ToString());
}
else
{
lblFinished.Text = "Nothing to resize";
}
}
}
Hope this helps!
If anyone's curious, I kinda figured it out but I'm not sure if that's good programming or any way to deal with the issue.
I created a new thread like so:
Thread t = new Thread(do_checks);
t.Start();
and put away all of the parallel stuff inside of do_checks().
Seems to be doing okay.
One problem with your code is that you're calling FinishedProxies.Add from multiple threads concurrently. That's going to cause a problem because List<T> isn't thread-safe. You'll need to protect it with a lock or some other synchronization primitive, or use a concurrent collection.
Whether that causes the UI lockup, I don't know. Without more information, it's hard to say. If the proxies list is very long and checkProxy doesn't take long to execute, then your tasks will all queue up behind that Invoke call. That's going to cause a whole bunch of pending UI updates. That will lock up the UI because the UI thread is busy servicing those queued requests.
This is what I think might be happening in your code-base.
Normal Scenario: You click on button. Do not use Parallel.Foreach loop. Use Dispatcher class and push the code to run on separate thread in background. Once the background thread is done processing, it will invoke the main UI thread for updating the UI. In this scenario, the background thread(invoked via Dispatcher) knows about the main UI thread, which it needs to callback. Or simply said the main UI thread has its own identity.
Using Parallel.Foreach loop: Once you invoke Paralle.Foreach loop, the framework uses the threadpool thread. ThreadPool threads are chosen randomly and the executing code should never make any assumption on the identity of the chosen thread. In the original code its very much possible that dispatcher thread invoked via Parallel.Foreach loop is not able to figure out the thread which it is associated with. When you use explicit thread, then it works fine because the explicit thread has its own identity which can be relied upon by the executing code.
Ideally if your main concern is all about keeping UI responsive, then you should first use the Dispatcher class to push the code in background thread and then in there use what ever logic you want to speedup the overall execution.
if you want to use parallel foreach in GUI control like button click etc
then put parallel foreach in Task.Factory.StartNew
like
private void start_Click(object sender, EventArgs e)
{
await Task.Factory.StartNew(() =>
Parallel.ForEach(YourArrayList, (ArraySingleValue) =>
{
Console.WriteLine("your background process code goes here for:"+ArraySingleValue);
})
);
}//func end
it will resolve freeze/stuck or hang issue

Dispatcher.Invoke from a new thread is locking my UI

i'm using wpf, there's a button on my ui.
when the user clicks it, i have a for loop that runs a new method, on a new thread using autoresetevent.
in that method on that new thread, i'm using a label, let's call it lblStatus. i want to update that label on this thread that's not on the ui. using wpf, i have to use Dispatcher.Invoke.
here's a sample of my code:
Thread thread= new Thread(StartLooking);
thread.Start();
_waitHandle.WaitOne();
private void StartLooking(object value)
{
if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)
{
lblStatus.Content = "Scanning>...";
}
else
{
lblStatus.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => lblStatus.Content = "Scanning>>>>>"));
}
_waitHandle.Set();
}
the program just stops here. it doesn't change the content of the label, it returns to my ui, but blocks it.
i've tried
lblStatus.Dispatcher.Invoke(DispatcherPriority.Normal, new LblStatusThreadCheck(lblStatusThreadCheck), "Scanning...");
as well, but that isn't working also. any ideas?
The problem is that you're making it impossible for this to execute, since you're using Invoke.
Dispatcher.Invoke will not return until the UI thread processes. However, you've blocked the UI thread by calling _waitHandle.WaitOne();, and don't set the wait handle until AFTER this processes. The two effectively cause a dead lock.
If you switch this to use BeginInvoke instead, the UI will queue the element, the wait handle will set, THEN the label will update. It will work and not block, however.
Since the two previous posts already cover the problem in your code, just a suggestion: instead of
if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)
try using
if (!lblStatus.CheckAccess())
It's cleaner and has the exact intent you want. Just read about it here.
You probably want to use BeginInvoke instead. Invoke will block the thread that called it until the UI thread has run the Action, and since you're setting the priority to Background, this could take some time.
Best solution I have found for .net 4.5+ is using SynchronizationContext Post
Example (Task.Run's can be as many as you want in parallel accessing UI):
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var context = SynchronizationContext.Current;
Task.Run(() =>
{
var i = 0;
while (true)
{
context.Post((tmp) =>
{
uiText.Text = $"{i}";
}), this);
Thread.Sleep(1000);
i++;
}
});
}

WPF MVVM Multithreading Issue

I've seen other issues similar to mine, but i haven't seen any that i can apply to make my code work.
So i'm new to MVVM, and i'm trying to get some stuff that's executing in a background thread to update on my UI. What i'm noticing is that the first time bring up the UI, and the background thread executes the first time, if the collection is IEnumerable<> the UI isn't fully updated against backing data. If the collection is ObservableCollection<>, it throws an error.
From what i've read, changes to collections need to be executed on the dispatcher thread, but OnPropertyChanged() calls do not. So someone, please tell me how this could be happening:
I'm altering my _Printers observable collection:
foreach (PrinterViewModel pv in _Printers)
{
DispatcherExec(() =>
{
var abilities = from x in _ServerData.Types
select new PrinterAbility(
new PrintableType() { ID = x.ID, Name = x.Name, NumInProcUnit = x.NumInProcUnit, PrintersMappedTo = x.PrintersMappedTo, SysName = x.SysName },
x.PrintersMappedTo.Contains(pv.Printer.ID)
);
pv.Printer.SetAbilities(abilities);
});
My DispatcherExec looks like so:
private void DispatcherExec(Action action)
{
//Dispatcher.Invoke((Action)delegate
//{
// action.BeginInvoke(null, null);
//}, null);
Dispatcher.CurrentDispatcher.Invoke((Action)delegate
{
action.Invoke();
}, null);
}
And here's the SetAbilities code that fails:
public void SetAbilities(IEnumerable<PrinterAbility> abilities)
{
if (log.IsInfoEnabled)
log.Info("SetAbilities(IEnumerable<PrinterAbility> abilities): called on printer "+Name);
List<PrinterAbility> l = new List<PrinterAbility>();
abilities.ForEach(i =>
{
i.PrinterAbilityChanged += new PrinterAbilityChangedEventHandler(OnPrinterAbilityChanged);
l.Add(i);
}
);
lock (_Abilities)
{
foreach (PrinterAbility pa in l)
_Abilities.Add(pa);
}
if (log.IsDebugEnabled)
log.Debug("SetAbilities(IEnumerable<PrinterAbility> abilities): leaving");
}
On the _Abilities.Add(pa) observable collection add it says "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread." I'm thinking, "are you joking?"
Further, I would think that a change to an object in the observable collection would automatically make it call OnCollectionChanged(), is that right?
Thanks in advance everyone.
Using Dispatcher.CurrentDispatcher is not something you should do from a BG thread. You need to use the Dispatcher for a DependencyObject-derived object that has been created on the UI thread.
Also, you're iterating over *ViewModel objects (PrinterViewModel) from within a BG thread. This really goes against MVVM. Your model should be doing asynchronous stuff, and your ViewModel(s) should be handling those asynchronous operations in a way that the view can consume (by marshalling to the proper thread via the Dispatcher).
Also, you're closing over a loop variable (pv). Bad, bad. This (depending on the order of execution) could mean that by the time the dispatcher comes around, you'll get multiple pv.Printer.SetAbilities(...) calls on the same PrinterViewModel instance. Create a local variable inside the loop and use that within your anonymous method to avoid this problem.
You should use the Dispatcher associated with any of your WPF controls, not the Dispatcher.CurrentDispatcher for the background thread.
Also
Dispatcher.CurrentDispatcher.Invoke((Action)delegate
{
action.Invoke();
}, null);
is redundant, it should be
wpfDispatcher.Invoke(action, null);
And finally for the first block, you should usually avoid passing loop variables to the lambdas, use temporary assignment trick to get around these sneaky closure problems. Almost certain that it's not the problem in this case though.

Categories

Resources