UI not updating when TableAdapter.Fill is called from another Thread - c#

I'm developing an MDI application in C# with .NET 4.0.
Each MDI child will be a form with tabs that contains GroupBoxes with a DataGridView.
I implemented a class that is used to manage Threads.
This is the StartNewThread method in my ThreadManager class
public string StartNewThread(ThreadStart threadMethod, string threadName)
{
try
{
Thread thread = new Thread(() => threadMethod());
thread.Name = threadName + " (" + _threadCount++.ToString("D4") + ")";
thread.Start();
_threadList.Add(thread.Name, thread);
return thread.Name;
}
catch (Exception ex)
{
//Log and manage exceptions
}
return null;
}
To create the DataGridViews I used some Wizard component from Oracle Developer Tools for VS library. So, after creating the DataSource and so the DataSet, then I used drag&drop from DataSource tree to drag tables and automatically create DataGridViews.
This is the actual working code, behind the child form, automatically created.
public partial class ScuoleNauticheForm : Form
{
public ScuoleNauticheForm()
{
InitializeComponent();
}
private void ScuoleNauticheForm_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.PERSONALE' table. You can move, or remove it, as needed.
this.PersonaleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.PERSONALE);
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.NATANTI' table. You can move, or remove it, as needed.
this.NatantiTableAdapter.Fill(this.DEVRAC_NauticheDataSet.NATANTI);
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.SCUOLE' table. You can move, or remove it, as needed.
this.ScuoleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.SCUOLE);
}
}
What I want to do now is manage all the load/query/insert/update/delete operations on separated threads. For now I tried to create a new Thread to load the data.
This i what I tried.
public partial class ScuoleNauticheForm : Form
{
private readonly ThreadManager _threadManager;
public ScuoleNauticheForm()
{
InitializeComponent();
_threadManager = ThreadManager.GetInstance();
}
private void ScuoleNauticheForm_Load(object sender, EventArgs e)
{
_threadManager.StartNewThread(LoadData, "LoadData");
}
#region DataBind
private void LoadData()
{
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.PERSONALE' table. You can move, or remove it, as needed.
this.PersonaleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.PERSONALE);
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.NATANTI' table. You can move, or remove it, as needed.
this.NatantiTableAdapter.Fill(this.DEVRAC_NauticheDataSet.NATANTI);
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.SCUOLE' table. You can move, or remove it, as needed.
this.ScuoleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.SCUOLE);
}
#endregion
}
It works only for half... There's no errors or exceptions, but if I load data that way, using a different Thread, the DataGridviews doesn't update and I don't see any data when opening the form, even if I move or resize it. Otherwise, using the automatically generated code, the DataGridViews are populated correctly.
But, since the wizard also add a navigation bar to the form to navigate through records, I noticed that it works, because it counts the correct number of records and I can use the arrows (first, previous, next, last) to move across records.
Here is an image showing my form.
See the navigation bar that is showing the correct number of total records (14) and allows me to navigate through them.
Do I need to use delegates? If so, I think it would be a mess... how many delegates should I create and for those methods? Or is there another solution?
-- UPDATE 1 --
I know that UI threads are automatically managed by .NET and so the programmer don't need to manage them with code. So, should it be a problem of synchronization with the .NET UI thread built in management? Maybe my thread launched by Form.Load() interferes with the UI thread managed by the .NET?
-- UPDATE 2 --
I tried to implement the solution proposed by faby. I replaced my Thread logic with Task logic. The behaviour of the application is the same, so everything that was working with Thread is now working also with Task. But the problem still remains. Since I'm on .NET 4.0 and not .NET 4.5, I could not use async and await. So I don't know if with that approach the UI will work correctly or not.
Any other suggestion valid for .NET 4.0?

do you consider the option of BackgroundWorker Class ?
implementing DoWork and ProgressChanged you can do in DoWork what you are doing in background thread and in ProgressChanged you can update the UI
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
//long running task
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//update the UI components
}
update 1
another solution could be something like this
public Task LoadDataAsync()
{
return Task.Factory.StartNew( () =>
{
//code to fill your datagridview
});
}
then
public async Task ChangeUIComponents()
{
await LoadDataAsync();
// now here you can refresh your UI elements
}
update 2
to use async/await with framework 4.0 try with this NugetPackage (Microsoft.Bcl.Async)

I finally found a solution without using async/await and other libraries.
The problem was that I was executing the Fill() method of TableAdapter inside a new Task and so I needed to use InvokeRequired to set the binding source data source to the DataTable within the right thread.
So I used delegates. I changed the method called on the new Task and make it call 3 other methods (one for each DataGridView to fill) that call Fill() implementing the InvokeRequired check.
Now I see the creation of the UI and then, after a couple of seconds, the asynchronous filling of the DataGridViews.
This article was useful: Load data from TableAdapter async
Thanks to #faby for the suggestion to use Task instead of Thread. It was not the solution but it is a better way to do Threading.
Here's the final working code.
public partial class ScuoleNauticheForm : Form
{
private readonly TaskManager _taskManager;
public ScuoleNauticheForm()
{
InitializeComponent();
_taskManager = TaskManager.GetInstance();
}
private void ScuoleNauticheForm_Load(object sender, EventArgs e)
{
_taskManager.StartNewTask(LoadData);
}
#region Delegates
public delegate void FillPersonaleCallBack();
public delegate void FillNatantiCallBack();
public delegate void FillScuoleCallBack();
#endregion
#region DataBind
private void LoadData()
{
FillPersonale();
FillNatanti();
FillScuole();
}
public void FillPersonale()
{
if (PersonaleDataGridView.InvokeRequired)
{
FillPersonaleCallBack d = new FillPersonaleCallBack(FillPersonale);
Invoke(d);
}
else
{
this.PersonaleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.PERSONALE);
}
}
public void FillNatanti()
{
if (NatantiDataGridView.InvokeRequired)
{
FillNatantiCallBack d = new FillNatantiCallBack(FillNatanti);
Invoke(d);
}
else
{
this.NatantiTableAdapter.Fill(this.DEVRAC_NauticheDataSet.NATANTI);
}
}
public void FillScuole()
{
if (ScuoleDataGridView.InvokeRequired)
{
FillScuoleCallBack d = new FillScuoleCallBack(FillScuole);
Invoke(d);
}
else
{
this.ScuoleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.SCUOLE);
}
}
#endregion
}
-- Update 1 --
If the methods to call by the new Task are void and without any parameters, you can simplify a bit the above code by using Invoke((MethodInvoker) MethodName). The behaviour of the application is the same.
Here's the simplified version of the code.
public partial class ScuoleNauticheForm : Form
{
private readonly TaskManager _taskManager;
public ScuoleNauticheForm()
{
InitializeComponent();
_taskManager = TaskManager.GetInstance();
}
private void ScuoleNauticheForm_Load(object sender, EventArgs e)
{
_taskManager.StartNewTask(LoadData);
}
#region DataBind
private void LoadData()
{
// Since Fill Methods are void and without parameters,
// you can use the Invoke method without the need to specify delegates.
Invoke((MethodInvoker)FillPersonale);
Invoke((MethodInvoker)FillNatanti);
Invoke((MethodInvoker)FillScuole);
}
public void FillPersonale()
{
this.PersonaleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.PERSONALE);
}
public void FillNatanti()
{
this.NatantiTableAdapter.Fill(this.DEVRAC_NauticheDataSet.NATANTI);
}
public void FillScuole()
{
this.ScuoleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.SCUOLE);
}
#endregion
}

Related

C# string becoming empty

Relatively new to C# and coding in general (first post here). I have a WinForms local application where some information is displayed to the user in a ReadOnly(true) RichTextBox. Almost all my classes need to send information to that RichTextBox. To simplify this process, I created a method inside a static class that uses a locked delegate to send the information to that RichTextBox. Here is a sample:
static class MyClass
{
public delegate void MessageReceivedEventHandler(string message);
public static event MessageReceivedEventHandler messageReceivedEventHandler;
public static void MessageBox(string message)
{
lock (messageReceivedEventHandler)
{
//Thread.Sleep(20);
messageReceivedEventHandler?.Invoke(message);
}
}
}
partial class MyForm : Form
{
public MyForm()
{
MyClass.messageReceivedEventHandler += OnMessageReceived;
}
private void OnMessageReceived(string message)
{
richTextBox1.Text = richTextBox1.Text.Insert(0, $" {message}\n");
}
private void Button1_click()
{
MyClass.MessageBox("This should be working!");
//Add more work here...
}
}
The code above would simply print "This should be working!" inside the RichtTextbox.
The problem is the text from richTextBox1 sometimes becoming empty. This issue seems to appear when the MessageBox method is being called in rapid succession. My assumption was that since I have diffent Tasks running at the same time (in other parts of my code), it probably is two Tasks attempting to use the same static ressource, hence the use of Lock. But I still have the issue.
Adding the Thread.Sleep(20) seems to fix the problem, but that is far from elegant/robust. It starts breaking up again when the time inside Sleep is <10ms.
Edit 1:
To clarify what I mean by "string becoming empty", it means the text from richTextBox1 is == "" at some points, which should not happen since the code is always inserting the text, not replacing it. The OnMessageReceived method is the only place where action is taken on the RichTextBox text.
Edit 2:
I saw many questions related to the other tasks running. First, yes it is a multi-threaded application. The only relation between those tasks and my main form is the "print" function I wrote above. To give more context, this application is used to control the position of stepper motors relative to an electrical signal. When doing so, I need to print important information in my main form. This is why losing the information in my RichTextBox (where I print the information) is an issue. The possible reason of why I am losing the text inside that RichTextBox should be the focus of this thread.
Keep in mind that this is a personnal side project, and not a large scale application.
Thanks,
Laurent
There are multiple problems in your code.
First, you should not lock on a public object, since that allows other threads to lock on the same object, risking interlocking your threads. Second, your symptoms suggest multiple threads are trying to access the ressources. Rather than depending on complex thread locking code, you'd rather schedule UI operations on the UI context, which will allow calling adding message from background tasks.
The best way to do that is to that is by using Control.BeginInvoke()
You can't copy your form instance everywhere, so we'll expose a static method. You could make the class a singleton, but if you need multiple instances that won't work. I'll give a more versatile example. When the static method is called, you don't have access to the form instance anymore, so we'll use IOC pattern with an event and delegate.
Let's make a private static event that all instances will register a callback to in the constructor. When the static method raises the static event, all instances callback will be called. The callback will schedule a modification of its text box.
partial class MyForm : Form
{
private class MessageWriteRequestedEventArgs : EventArgs
{
public string Message { get; }
public MessageWriteRequestedEventArgs(string message)
{
Message = message;
}
}
private static event EventHandler<MessageWriteRequestedEventArgs> MessageWriteRequested;
public MyForm()
{
MessageWriteRequested += OnMessageWriteRequested;
}
public static void WriteMessage(string message)
{
MessageWriteRequested?.Invoke(this, new MessageWriteRequestedEventArgs(message));
}
private void OnMessageWriteRequested(object sender, MessageWriteRequestedEventArgs e)
{
richTextBox1.BeginInvoke(() => WriteMessageSafe(e.message));
}
private void WriteMessageSafe(string message)
{
richTextBox1.Text = richTextBox1.Text.Insert(0, $" {message}\n");
}
private void Button1_click()
{
// you're on ui context, you're safe to access local ui resources
WriteMessageSafe("This should be working!");
// if you have multiple MyForm instances, you need to use the event
WriteMessage("Broadcasting my tralala");
}
}
If you need to write to the textbox from anywhere else :
// do stuff
MyForm.WriteMessage("Ho Ho Ho !");
.NET already includes a class for reporting progress (or any other information) from an asynchronous operation in a thread-safe manner, Progress< T>. It doesn't need locking and even better, it decouples the sender and receiver. Many long-running BCL operations accept an IProgress<T> parameter to report progress.
You haven't explained what's going on in the form, or what task is reporting the data. Assuming the producer is another method in the same form, you could create a Progress<T> instance in the same method that starts the async operation, eg :
async void Button1_Click()
{
var progress=new Progress<string>(ReportMessage);
ReportMessage("Starting");
await Task.Run(()=>SomeLongOp(progress));
ReportMessage("Finished");
}
void SomeLongOp(IProgress<string> progress)
{
for(int i=0;i<1000000;i++)
{
...
progress.Report($"Message {i}");
...
}
}
void ReportMessage(string message)
{
richTextBox1.Text = richTextBox1.Text.Insert(0, $" {message}\n");
}
By using IProgress< T>, the SomeLongOp method isn't tied to a specific form or global instance. It could easily be a method on another class
Publishing lots of messages
Let's say you have a lot of workers, doing a lot of things, eg monitoring a lot of devices, and want all of them to publish messages to the same Log textbox or RTF box. Progress< T> "simply" executes the reporting delegate or event handler on its original sync context. It doesn't have an asynchronous Report method, nor can it queue messages. In a really high-traffic environment, the synchronization switch can delay all workers.
The built-in answer to this is to use one of the pub/sub classes like ActionBlock< T> or a Channel.
An ActionBlock< T> processes the messages in its input queue in order, using a worker task that runs on the ThreadPool by default. This can be changed by specifying a different TaskScheduler in its execution options. By default, its input queue is unbounded.
One could use an ActionBlock to receive messages from multiple workers and display them on a textbox. The block can be created in the constructor, and passed to all workers as an ITargetBlock<T> interface :
ActionBlock<string> _logBlock;
public MyForm()
{
var options=new ExecutionDataFlowBlockOptions {
TaskScheduler=TaskScheduler.FromCurrentSynchronizationContext();
};
_block=new ActionBlock<string>(ReportMessage,options);
}
Now the fun begins. If the workers are created by the form itself, the workers can publish to the block directly :
public async void Start100Workers_Click(...)
{
var workers=Enumerable.Range(0,100)
.Select(id=>DoWork(id,_block));
await Task.WhenAll(workers);
}
async Task DoWork(int id,ITargetBlock<string> logBlock)
{
.....
await logBlock.SendAsync(message);
...
}
Or the block could be exposed through a public property, so other classes/forms in the application can post to it.
public ITargetBlock<string> LogBlock=>_block;
I'm going to show a simple way to do what I think you're after.
I started with a .NET Core 3.1 Win forms application. I added a rich text control to the form. I added a button to the form.
I added a TaskCompletionSource as a instance property - this will be used to control the tasks acting as workers which you described.
CancellationTokenSource sharedCancel = new CancellationTokenSource();
I created an interface to represent something that accepts messages as you described:
public interface IMyMessageSink
{
Task ReceiveMessage(string message);
}
I made my form support this interface.
public partial class Form1 : Form, IMyMessageSink
The ReceiveMessage method looks like this:
public Task ReceiveMessage(string message)
{
if(this.sharedCancel == null || this.sharedCancel.IsCancellationRequested)
return Task.FromResult(0);
this.Invoke(new Action<Form1>((s) => this.richTextBox1.Text = this.richTextBox1.Text.Insert(0, $"{message}\n")), this);
return Task.FromResult(0);
}
You'll see the Invoke handles the synchronization back to the UI thread.
This should probably use BeginInvoke and then convert the APM to async tasks which you can read about here. But for an SO answer the above simple code will suffice.
Also note there's no error handling. You'll want to add that to your generator and to the button handler.
Next I created a class to represent something that creates messages. This class takes the interface created and the cancellation token. It looks like this:
public class MyMessageGenerator
{
CancellationToken cancel;
IMyMessageSink sink;
public MyMessageGenerator(CancellationToken cancel, IMyMessageSink sink)
{
this.cancel = cancel;
this.sink = sink;
}
public async Task GenerateUntilCanceled()
{
try
{
while (!this.cancel.IsCancellationRequested)
{
await sink.ReceiveMessage(this.GetHashCode().ToString());
await Task.Delay(5000, this.cancel);
}
}
catch (OperationCanceledException)
{ }
}
}
In the button handler we create the message generators.
async void button1_Click(object sender, EventArgs e)
{
if (null == this.sharedCancel)
return;
await Task.Run(() => new MyMessageGenerator(this.sharedCancel.Token, this).GenerateUntilCanceled());
}
Finally I added an override for the form closing event:
protected override void OnClosing(CancelEventArgs e)
{
if (null != this.sharedCancel)
{
this.sharedCancel.Cancel();
this.sharedCancel.Dispose();
this.sharedCancel = null;
}
base.OnClosing(e);
}
If the application becomes larger and more complex you would likely benefit by adding services exposed using a DI container. You can read about adding DI to a winforms app here.

Preventing GUI hangs in WPF

I am relatively new to C# WPF projects and I have an issue with my code whereby the user interface "locks up" whilst I am running a task. Here is the relevant code:
public partial class MainWindow : Window {
// handles to ManagedDLAContainer objects
private ManagedDLA2DContainer dla_2d;
public MainWindow() {
InitializeComponent();
// initalise aggregate containers
dla_2d = new ManagedDLA2DContainer();
}
private void GenerateAggregate() {
// generate the 2D aggregate of size given by particles_slider value
Dispatcher.Invoke(new Action(() => { dla_2d.Generate((uint)particles_slider.Value); }));
// TODO: add particles to "canvas" on GUI as they are generated
}
private void GenerateButtonClick(object sender, RoutedEventArgs e) {
// set the coefficient of stickiness of aggregate
// to current value of stickiness_slider
dla_2d.SetCoeffStick(stickiness_slider.Value);
// start asynchronous task calling GenerateAggregate method
Task.Factory.StartNew(() => GenerateAggregate());
}
}
Here ManagedDLA2DContainer is a managed C++/CLI wrapper for some native, unmanaged C++ code and stickiness_slider is simply a Slider element of the WPF interface; whilst, similarly, particles_slider is another Slider element of the interface. I should note that the following code also results in the GUI hanging:
public partial class MainWindow : Window {
// lock object for multi-threading tasks
private static readonly object locker = new object();
// handles to ManagedDLAContainer objects
private ManagedDLA2DContainer dla_2d;
public MainWindow() {
InitializeComponent();
// initalise aggregate containers
dla_2d = new ManagedDLA2DContainer();
}
private void GenerateAggregate() {
// lock around aggregate generation
lock (locker) {
// generate the 2D aggregate of size given by particles_slider value
Dispatcher.Invoke(new Action(() => { dla_2d.Generate((uint)particles_slider.Value); }));
// TODO: add particles to "canvas" on GUI as they are generated
}
}
private void GenerateButtonClick(object sender, RoutedEventArgs e) {
// set the coefficient of stickiness of aggregate
// to current value of stickiness_slider
dla_2d.SetCoeffStick(stickiness_slider.Value);
Thread agg_gen_thread = new Thread(GenerateAggregate);
agg_gen_thread.Start();
agg_gen_thread.Join();
}
}
Any information which could help me understand what I might be doing wrong here is appreciated.
Also, if you are curious then all the code for this project is available here: https://github.com/SJR276/DLAProject
You start new thread in which you run GenerateAggregate. Then immediatly you dispatch all work back to user interface thread via Dispatcher.Invoke. So basically you are running long running task on UI thread and it blocks.
Instead, you should dispatch to UI thread only operations that require that (which update UI controls for example), not the whole operation.
Suppose your Generate function looks like this:
void Generate() {
MakeCpuIntensiveWork();
UpdateUIWithResults();
}
You only need to dispatch second part to UI thread. In your case, as we found out in comments, the only UI part is getting value of a slider. So you can split like this:
private void GenerateAggregate()
{
uint sliderValue = 0;
// generate the 2D aggregate of size given by particles_slider value
Dispatcher.Invoke(() => { sliderValue = (uint)particles_slider.Value; });
dla_2d.Generate(sliderValue);
// TODO: add particles to "canvas" on GUI as they are generated
}

How does UI thread know about data on another thread?

I came across a situation which puzzled me at work today which I have simplified in the following code. This code builds and throws no exceptions during debug.
Suppose I have a WinForms app. In my main UI thread I spin off another thread which instantiates an object which in turn holds reference to a control (label1 in my example). I then call a method on my object (SetLabelText) which passes it's execution back onto the UI thread if required.
What stumped me was how, when we are back in the UI thread and executing SetLabelText, is .net CLR able to access the labelText variable when we are executing on a thread (ie the UI thread) which did not create the instance of Thing.
public partial class Form1 : Form
{
delegate void DoSomethingDelegate();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var t = new Thread(DoSomethingWithThing);
t.Start();
}
private void DoSomethingWithThing()
{
var thing = new Thing(this.label1);
thing.SetLabelText();
}
}
public class Thing
{
private Label label;
private string labelText = "new value";
delegate void SetLabelTextDelegate();
public Thing(Label label)
{
this.label = label;
}
public void SetLabelText()
{
if (this.label.InvokeRequired)
{
var setLabelDel = new SetLabelTextDelegate(SetLabelText);
this.label.Invoke(setLabelDel);
}
else
{
this.label.Text = this.labelText;
}
}
}
References to objects are available on any thread.
Threads are not sand-boxed from each other. They share resources unless you explicitly create non-shared resources.
Threads are execution contexts. Think of your application as a kitchen and each thread as a chef. They can work at the same time but if two of them try to use the same knife at the same time, things get messy. This is why c# has the lock keyword and other synchronization mechanisms.
WinForms has restrictions on access to controls because of the way WinForms renders.

Thread contained inside class

I'm writing a simple Windows forms application to get me into the swing of things with Threads. So far what I have is working, but what I would like to do is contain it all in a seperate class rather than directly in my forms code.
I have a background thread that starts and retrieves data from a database. I then display that data in to a listbox.
private delegate void UpdateListValues(List<ListBoxItem> itemList);
private void form_main_Shown(object sender, EventArgs e)
{
// Set the loading text.
list_selection.Items.Add(ListHelpers.LoadingItem());
// Start the data access on a seperate thread.
Thread worker = new Thread(GetInvoicingData);
worker.IsBackground = true;
worker.Start();
}
private void GetInvoicingData()
{
// Query database
List<ListBoxItem> values = DAC.GetInvoicingAccounts();
// Display results
BeginInvoke(new UpdateListValues(DisplayList), new object[] { values });
}
private void DisplayList(List<ListBoxItem> itemList)
{
// Display each result
list_selection.Items.Clear();
for (int i = 0; i < itemList.Count; i++)
{
list_selection.Items.Add(itemList[i]);
}
}
The problem is that in the DisplayList method, I won't be able to access the list box (list_selection) because it's part of the form class. Does anyone have any suggestions on how I can do this.
Also, I'm new to threading so feel free to tell me I'm doing it absolutely wrong. I just used the example from http://www.codeproject.com/Articles/23517/How-to-Properly-Handle-Cross-thread-Events-and-Upd to get me to where I am now.
Thanks
How about something like this:
// Added the form's class declaration to highlight separation of thread code into a separate class, but may not be exactly the same as yours depending on naming
public class Form1 : Form
{
private readonly DataRetriever _dataRetriever;
private void form_main_Shown(object sender, EventArgs e)
{
// Set the loading text.
list_selection.Items.Add(ListHelpers.LoadingItem());
// Create the DataRetriever, and provide it with a delegate to DisplayList for returning data
_dataRetriever = new DataRetriever(DisplayList);
// Start retrieving data on a separate thread...
_dataRetriever.GetData();
}
private void DisplayList(List<ListBoxItem> itemList)
{
if (InvokeRequired)
{
// Ensure the update occurs on the UI thread
Invoke((Action)(() => DisplayList(itemList)));
return;
}
// Display each result
list_selection.Items.Clear();
foreach (var item in itemList)
{
list_selection.Items.Add(item);
}
}
}
// Separate class to hold thread code
public class DataRetriever
{
public delegate void UpdateCallbackDelegate(List<ListBoxItem> itemList);
private readonly UpdateCallbackDelegate _updateCallback;
public DataRetriever(UpdateCallbackDelegate updateCallback)
{
_updateCallback = updateCallback;
}
public void GetData()
{
var thread = new Thread(GetInvoicingData)
{
IsBackground = true
};
thread.Start();
}
private void GetInvoicingData()
{
// Not sure whether "DAC" is a static class, if it needs to be constructed
// in the DataRetriever's constructor, or passed to it as a parameter
_updateCallback(DAC.GetInvoicingAccounts());
}
}
As you can see, all the thread code is now in a separate class DataRetriever, and a delegate provided when constructing it to enable the retrieved data to be passed back to the form once the retrieval is complete. The method that handles the callback ensures that the call is marshalled to the UI thread to prevent cross-thread exceptions.
I would like to point out that this is not presented as the "best" way to do this, but merely as an answer to the question (how to separating threading code into a separate class). As others have mentioned, there are already mechanisms in place to do this sort of thing (e.g. BackgroundWorker). Some complexity has been omitted for clarity. For example, in the implementation presented here, if you were to call GetData() multiple times (with each call occurring before the previous ones have returned their data), you would have multiple queries occurring simultaneously, and as they are running asynchronously, may return their data in an arbitrary order. This may or may not be an issue in your case.

c# threads and properties second part

I have this code:
public partial class FrmPrincipal : Form
{
private Image imagen;
public FrmPrincipal()
{
InitializeComponent();
...
}
private void menuItem1_Click(object sender, EventArgs e)
{
Thread t = new Thread(RequestImage);
t.Start();
}
private void RequestImage()
{
try
{
...
// I want to update this.token
this.imagen = retrieveImageFromWebService();
...
}
catch (Exception ex)
{
...
}
}
}
How can I update image? I want to save a copy of image to update a pictureBox when user needs it.
Thanks!
Your code will work perfectly well is it is. However, if you want to read the value from another thread and make sure you always get the most recent value, you should either make it volatile or acquire a lock each time you read or write it.
See the memory model section of my threading article for more information.
The code you have there should work fine. If you are using token in the other thread through, you'll probably want to syncronize gets and sets to avoid data corruption:
private string token {
[MethodImpl(MethodImplOptions.Synchronized)] get;
[MethodImpl(MethodImplOptions.Synchronized)] set;
}
This syncronization method is not 100% safe in all circumstances, but for your purposes it should work

Categories

Resources