Dispatcher Task /Action Completion - c#

I have an application which fetches data form the server and gives the result to the user .
The data being fetched is quite large which blocks the UI for sometime. I am using dispatcher to make it asynchronous.
Here is the code snippet :-
private void GetData(object sender, RoutedEventArgs e)
{
List<result> data=new List<result>;
DispatcherObject obj= Dispatcher.BeginInvoke(DispatcherPriority.Background,(ThreadStart)delegate
{
data =Data.fetch_data(id, name, url);
});
if(obj.Completed){
MessageBox.Show("Done!");
}
}
But the code gives an error saying
"Cannot implicitly convert type
'System.Windows.Threading.DispatcherOperation' to
'System.Windows.Threading.DispatcherObject' ".
Is there anyway where user can notified when the background task gets completed?
Edit :-
Here is the async/await code
private async void GetData(object sender, RoutedEventArgs e)
{
Task<List<result>> T =Task<List<resultsummary>>.Factory.StartNew(() => data==Data.fetch_data(id, name, url));
await T;
}
But it gives an error "" The calling thread cannot access this object because a different thread owns it."

Calling Dispatcher.BeginInvoke does not enable you to perform asynchronous actions on a background thread unless you initialised the Dispatcher object on a background thread. So, instead of using a Dispatcher to do what you want, in order to fetch data on a background thread and then pass that data from the background thread to the UI thread, you can use the Task class.
Task.Factory.StartNew((Func<YourDataType>)delegate()
{
// Fetch data on background thread here (return YourDataType, whatever that is)
return DataAccessClass.GetData();
}).ContinueWith((Task<YourDataType> task) =>
{
// Update controls with result on UI thread here
YourUiProperty = task.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
Obviously, you'll need to replace the YourDataType type with whatever your data type is.

With .Net 4.5 and VS2013 i would recommend the Usage of async and await.
By executing the fetch_data operation on the Dispatcher you are explicitly blocking the UI Thread with your fetch Operation.
private async void GetData(object sender, RoutedEventArgs e)
{
// fetch the data in a Background Task and release the UI Thread.
List<result> data = await Task.Run<List<result>>(() => Data.fetch_data(id, name, url));
// when completed return to the UI Thread.
this.MyListBox.ItemsSource = data;
}
Even better would be to build an async Version of your fetch_data Operation that Returns a Task<IEnumerable<result>>

Related

keep windows.forms controls in same Main thread

So, I have a win form that calls a method
dvm.SetVoltage(excitationVoltage, rigNo);
which runs a task in another class
public void SetVoltage(double voltage, int rigNum)
{
Task.Run(() => ReadDVMWorker());
}
Once the worker is finished (voltage set) it triggers an event In the main Form1.cs
private void dvmVoltageSet(object sender, VoltageEventArgs e)
{
VoltageSet = e.VolSet;
TestLvdtNull();
}
Calling TestLvdtNull method:
private void TestLvdtNull()
{
tbMessage.Location = new Point((int)(x / 2 - 250), 150);
}
As soon as the tbMessage line is reached it causes an exception because it has started another thread other than the one tbMessage was created in, how can I prevent it from starting a new thread and continue using the Main thread please?
I have looked at singlethreadsynchronizationcontext, but couldn't make it compile and I know that you can invoke:
tbMessage.Invoke((Action)delegate
{
tbMessage.Location = new Point((int)(x / 2 - 250), 150);
});
But I have many controls with many attributes changing, there must be a way to keep the UI on the main thread?
All UI controls are created at one thread. That is by design in many UI frameworks. After you finish your task you have to return to the UI thread to access UI controls.
One option mentioned in comments is to use async/await where the part of the method after await keyword is executed on the same thread as was before the async method.
// UI thread
await ReadDVMWorker(); // executed at ThreadPool
// UI thread
If you prefer to stay with Task, you can use ContinueWith method with correct TaskScheduler parameter, which ensures that you're back to UI thread. Eg. TaskScheduler.FromCurrentSynchronizationContext()
Async/await attempt code:
private async void button1_Click(object sender, EventArgs e)
{
// Call the method that runs asynchronously.
string result = await WaitAsynchronouslyAsync();
// Display the result.
textBox1.Text += result;
}
//The following method runs asynchronously.The UI thread is not
//blocked during the delay.You can move or resize the Form1 window
//while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
await dvm.SetVoltage(5, rigNo); //Task.Delay(10000);
return "Finished";
}
You could have a method to update arbitrary controls
private void dvmVoltageSet(object sender, VoltageEventArgs e)
{
VoltageSet = e.VolSet;
TestLvdtNull(tbMessage);
TestLvdtNull(tbMessage2);
}
private void TestLvdtNull(Control control)
{
control.BeginInvoke((MethodInvoker)delegate()
{
control.Location += new Point((int)(x / 2 - 250), 150);
});
}
After trying several different ways to solve the problem, I solved the problem by using SynchronizationContext.
This grabs the SyncronizationContext of the thread:
private SynchronizationContext _synchronizationContext;
SynchronizationContext uiContext = SynchronizationContext.Current;
Then after running my task in another class, where previously I was getting an cross thread call exception, I call the method that wants use the same UI thread:
uiContext.Post(MethodToCallOnTheSameUIThread, "string");
After this I can modify and update my textboxes and other controls!
You can check the thread id by:
int id = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Thread: " + id);
With thanks to Mike Peretz and his CodeProject

WPF Dispatcher Thread Freeze main window

Instead of working in the background - this code still freeze my program:
private void button_Click(object sender, RoutedEventArgs e)
{
this.Dispatcher.BeginInvoke(new Action(() =>
{
Thread.Sleep(5000);
label.Content = "Done";
}), DispatcherPriority.Normal);
}
I have tried with Thread/Tasks, thread example:
private void button_Click(object sender, RoutedEventArgs e)
{
var t = new Thread(new ThreadStart(runtask));
t.Start();
}
private void runtask()
{
this.Dispatcher.BeginInvoke(new Action(() =>
{
Thread.Sleep(5000);
label.Content = "Done";
}), DispatcherPriority.Normal);
}
Task example:
private void button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() =>
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
{
Thread.Sleep(5000);
label.Content = "Done";
}));
});
}
And still my program is freezing. Any suggestions?
From the documentation of the Dispatcher class:
Provides services for managing the queue of work items for a thread.
From the documentation of Dispatcher.BeginInvoke:
Executes the specified delegate asynchronously with the specified arguments on the thread that the Dispatcher was created on.
Here "asynchronously" refers to the secondary thread, not the main one. Because the main one is owned by the main Dispatcher. That means that every call of Invoke or BeginInvoke on that Dispatcher, from whatever Thread, will put the invoked Action in the queue of operations that the main Thread must execute, but from the point of view of the main Thread they will be executed synchronously, one after the other.
For example, if you put 3 Action like Thread.Sleep(1000); within 10 ms on the Dispatcher, whether with Invoke or BeginInvoke and from whether Thread, that Dispatcher will make the UI Thread to execute the 3 Action synchronously, so they will take a total of 3000 ms.
Maybe the documentation about BeginInvoke could have been written better, like:
Executes the specified delegate with the specified arguments on the thread that the Dispatcher was created on. The specified delegate is executed asynchronously from the point of view of the calling thread.
Now... Invoke or BeginInvoke?
Using Invoke, the secondary Thread is saying to the Dispatcher: let's execute this on the main Thread, and don't dare to return until your thread's job has finished. Then and only then I will continue.
For example, if you write this:
this.Dispatcher.Invoke(new Action(() =>
{
Thread.Sleep(5000);
Debug.WriteLine("After Sleep");
}));
Debug.WriteLine("Continuation on secondary Thread");
The Console will print after ~ 5000 ms:
"After Sleep"
"Continuation on secondary Thread"
Using BeginInvoke, instead, the Thread is saying: "hey, Dispatcher, queue this operation on the main Thread, but return as soon as possible so I can continue my job immediately".
In this case the Console will print immediately:
"Continuation on secondary Thread"
And after ~ 5000 ms:
"After Sleep"
Now, if your purpose is to execute some heavy operation on the background, you should learn about the async/await pattern, available from .NET 4.5 and C# 5.0.
In your example, I would write:
private async void button_Click(object sender, RoutedEventArgs e)
{
await Task.Delay(5000); // await a heavy operation executed in background
label.Content = "Done"; // control back to the UI Thread that executes this
}
You can use this small extension if your UI access is the last of your method.
https://mitsufu.wordpress.com/2015/08/03/dispatcher-switchto/
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(async () =>
{
//heavy background operation
await Dispatcher.SwitchTo();
Title = "ok";
});
}

c# Async/Await not updating UI

I am learning to implement async/await on one of my task which takes a long time to complete. Once the task is completed, it should update the DGV.
private async void frmA_DragDrop(object sender, DragEventArgs e)
{
// Some codes to accept dropped files
// Run task asynchronously
Task<DataTable> task = Task.Run(() => PDF.CheckPDFs(Files, tblPDFs));
dgvPDF.DataSource = await task;
}
I found that after the task is completed, it is not updating the DGV. So, I added the InvokeRequired bit to manually update the table and also to prevent cross-thread exception.
// Update UI
if (dgvPDF.InvokeRequired)
{
dgvPDF.Invoke((MethodInvoker)(() => { dgvPDF.Refresh(); }));
}
else
{
dgvPDF.Refresh();
}
I also have a separate event handler which highlights the row red is a file is invalid:
private void dgvPDF_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
if (dgvPDF["Pages",e.RowIndex].Value.ToString() == "0")
dgvPDF.Rows[e.RowIndex].DefaultCellStyle.BackColor = System.Drawing.Color.MistyRose;
}
What happened is that the RowAdded event handler is not triggered at all. If I made the code synchronous i.e removing await/async and invoke, everything works fine except with the UI freezing
My question is using InvokedRequired the correct way to update the UI in async await? What can I do to fix this issue?
no, you don't have to "invoke", the context will switch back after async method call complete, i.e., the statements after await will get executed in UI thread.

how to use return method in backgroundworker

i use background worker to call method but in return give me an Exception here is the code
private void Button_Click(object sender, RoutedEventArgs e)
{
BGW.RunWorkerAsync();
}
void BGW_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = new Drug_Class().Search(Filter);
}
class Drug_Class
{
public List<WorkSpaceVariable.Drug_List> Search(Expression<Func<db.Drug_Catalog, bool>> Filter)
{
using (db.PVDBDataContext PVDB=new PVDBDataContext ())
{
try
{
var DQuary = from D in PVDB.Drug_Catalogs.Where(Filter)
select new WorkSpaceVariable.Drug_List
{
Drugs_ID = D.Drugs_ID
}
return DQuary.ToList() ;//Exception The calling thread cannot access this object because a different thread owns it.
}
catch (Exception E)
{
return null;
}
}
}
i don't understand why i got this Exception may any one tell me whats wrong with my code ?
The Exception that you got really explains your problem... all you need to do is to read it:
The calling thread cannot access this object because a different thread owns it.
You should hopefully know that a BackgroundWorker works on a background thread, so you should be able to work out that the two threads mentioned would be this background thread and the main UI thread.
What it means is that you cannot manipulate UI objects that were declared on (and therefore owned by) the main UI thread on any other thread.
The solution is to not manipulate UI objects from the UI thread in your background thread. Instead, just manipulate the data in the background thread and when the BackgroundWorker has finished, then update the UI element by updating the data bound collection.

Update UI from thread in WinRT

Since the Windows 8 consumer preview was released a few days ago, I am working on the new WinRT (for Metro Applications) in C# and I had ported my self written IRC class to the new threading and networking.
The problem is: My class is running an thread for receiving messages from the server. If this happens, the thread is making some parsing and then firing an event to inform the application about this. The subscribed function then 'should' update the UI (an textblock).
This is the problem, the thread cannot update the UI and the invoker method that has worked with .NET 4.0 doesn't seem to be possible anymore. Is there an new workaround for this or even an better way to update the UI ? If I try to update the UI from the event subscriber i will get this Exception:
The application called an interface that was marshalled for a
different thread (Exception from HRESULT: 0x8001010E
(RPC_E_WRONG_THREAD))
The preferred way to deal with this in WinRT (and C# 5 in general) is to use async-await:
private async void Button_Click(object sender, RoutedEventArgs e)
{
string text = await Task.Run(() => Compute());
this.TextBlock.Text = text;
}
Here, the Compute() method will run on a background thread and when it finishes, the rest of the method will execute on the UI thread. In the meantime, the UI thread is free to do whatever it needs (like processing other events).
But if you don't want to or can't use async, you can use Dispatcher, in a similar (although different) way as in WPF:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => Compute());
}
private void Compute()
{
// perform computation here
Dispatcher.Invoke(CoreDispatcherPriority.Normal, ShowText, this, resultString);
}
private void ShowText(object sender, InvokedHandlerArgs e)
{
this.TextBlock.Text = (string)e.Context;
}
Here is an easier way to do it I think!
First capture your UI SyncronizationContext with the following:
var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();
Run your server call operation or any other background thread operation you need:
Task serverTask= Task.Run(()=> { /* DoWorkHere(); */} );
Then do your UI operation on the UISyncContext you captured in first step:
Task uiTask= serverTask.ContinueWith((t)=>{TextBlockName.Text="your value"; }, UISyncContext);
IMO I think "ThreadPool" is the recommended route.
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh465290.aspx
public static Task InvokeBackground(Func<Task> action)
{
var tcs = new TaskCompletionSource<bool>();
var unused = ThreadPool.RunAsync(async (obj) =>
{
await action();
tcs.TrySetResult(true);
});
return tcs.Task;
}

Categories