Cross-threaded operation not valid error in C# - c#

I'm pretty new to C# and all the threading stuff and I'm getting "Cross-threaded operation not valid error" at the moment.
Here are the relevant parts of the code:
private LinkedList<string> _statusList = new LinkedList<string>();
private void ReportToStatus(string message)
{
_statusList.AddLast(message);\
// textStatus is a textbox.
// And this is the exact line that is giving the error:
textStatus.Lines = _statusList.ToArray();
}
private void RunTest()
{
// ...
// Run the test in the background worker.
bgwTest.RunWorkerAsync(testCase);
}
private void bgwTest_DoWork(object sender, DoWorkEventArgs e)
{
TestCase testCase = e.Argument as TestCase;
// ...
// Run the test.
switch (testCase.TestType)
{
case "TestA": TestA(testCase);
break;
}
e.Result = testCase;
}
private void TestA(TestCase testCase)
{
// ...
PrintStatistic(statisticsForCoil, testCase.OutputFile);
}
}
private void PrintStatistic(int[] statistics, string outputFile)
{
// ...
ReportToStatus(result);
}
How should I proceed?

It looks like there might be a problem in _statusList. You can't write to it from a different thread, only read.
From MSDN
"The LinkedList class does not support chaining, splitting, cycles,
or other features that can leave the list in an inconsistent state.
The list remains consistent on a single thread. The only multithreaded
scenario supported by LinkedList is multithreaded read operations."
Also, you can't access the UI from a background thread. You need to use the dispatcher to invoke operations onto the UI thread. To do this your code will need to look like this
WPF
Application.Current.Dispatcher.Invoke(new Action(delegate
{
textStatus.Lines = _statusList.ToArray();
}));
WinForms
textStatus.Invoke(new Action(delegate
{
textStatus.Lines = _statusList.ToArray();
}));

The BackgroundWorker has a dedicated mechanism for updating the UI:
BackgroundWorker.ReportProgress. For example, in your code it could look like this:
private void ReportToStatus(string message)
{
_statusList.AddLast(message);
// textStatus is a textbox.
// And this is the exact line that is giving the error:
bgwTest.ReportProgress(0, _statusList.ToArray());
}
//Assuming this is the method handling bgwTest's ProgressChanged event
private void bgwTest_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
textStatus.Lines = (string[])(e.UserState);
}

You are trying to update the UI from your background worker which will cause that exception. You can use the Dispatcher to schedule an update instead - or more ideally use the background worker to do "background" work only, then do your UI updates when the RunWorkerCompleted event is raised.

Related

Raise Events to the UI thread from a Task

I have been trying to determine the proper approach to manipulate the UI from an asynchronous task. My application has become cluttered with threaded Invokes and BeginInvokes. I am trying to alleviate this clutter as well as provide a bit more responsiveness on the UI by taking advantage of C# async and await.
Somewhere on the UI thread I initialize IProgress event handler and pass it to an asynchronous function called DoInfiniteWorkAsync. This function runs infinitely in a background task but often has the need to update portions of the UI.
private void Form1_Load(object sender, EventArgs e)
{
// Create a UIEventHandler on our UI thread.
Progress<string> UIEventHandler = new Progress<string>();
UIEventHandler.ProgressChanged += UIEventHandler_CommandRaised;
// Pass the UIEventHandler to our Asyncronous task.
DoInfiniteWorkAsync(UIEventHandler);
}
void UIEventHandler_EventRaised(object sender, string e)
{
string eventMessage = e;
// Update UI based on the event message.
}
My DoInfiniteWorkAsync function uses the passed in UIEventHandler to report UIEventMessages while running its task.
private async Task DoInfiniteWorkAsync(IProgress<string> UIEventHandler)
{
await Task.Run(() =>
{
// 24/7 running tasks.
// Sets a UIEventMessage to signal UI thread to do something.
UIEventHandler.Report(UIEventMessage);
});
}
Is this the proper way to be updating the UI thread from a long running background thread? The fact that my event handler datatype (IProgress) is specifically directed at progress reporting is making me feel like I'm missing the point.
To bring in data from one asynchronous thread to another you have to invoke it.
Define in your class a field of property:
string _readData = null;
Then fill the string with the eventMessage and call a method to invoke the data.
string eventMessage = e;
_readData = eventMessage;
Msg();
This is the method Msg():
private void Msg()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(Msg));
}
else
{
textBox2.Text = textBox2.Text + Environment.NewLine + " >> " + _readData;
}
}

Background worker in windows form application

I have the following constellation:
MainForm.cs -> Including all my Form Elements
Program.cs -> includes the main part, which is a xmlreader/writer to alter xml attributes in xml files that can be as large as 4gb
So this little app works but of course the UI gets unresponsive and freezes which I want to avoid, I also hope to reduce the duration of this process on the way
I start the call of my xmlread/write method from a BtnClick event:
void BtnApplyChangesClick(object sender, EventArgs e)
{
Program p = Program.Instance;
pbApplyChanges.Minimum = 0;
pbApplyChanges.Step = 1;
Cursor.Current = Cursors.WaitCursor;
foreach(DataGridViewRow cr in dataGridView2.Rows)
{
pbApplyChanges.Maximum = dataGridView2.Rows.Count;
p.changeElements(cr.Cells["Filename"].Value.ToString(), txtTenant.Text, txtDate.Text, txtEvtId2.Text);
pbApplyChanges.PerformStep();
}
Cursor.Current = Cursors.Arrow;
MessageBox.Show("Job done");
}
In the call I use my singleton instance of Program.cs and my main Method there (changeElements) uses 4 String params, that are all taken from information in the Form! (I suppose this is kinda bad practice but it worked so far...)
When I tried to replace this method call with a backgroundWorker (itself made the method call then) I failed as the method call wasn't even made... I found out that UI elements can't be accessed from the BW thread, so I suppose this is also the reason for my method call not working?!
So how can I get this constellation to work? Do I have to pass all 4 string Params AND the class instance (of Program.cs) to the background worker? Is BW even the best tool for the job?
In general the BackgroundWorker shouldn't access any UI-Elements. It's an old advice in Winforms that accessing UI-Elements should just happen from the UI-Thread.
You can use the Background-Worker like this:
private void Main(string[] args)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += Bw_DoWork;
bw.RunWorkerCompleted += Bw_RunWorkerCompleted;
//Parameter you need to work in Background-Thread for example your strings
string[] param = new[] {"Text1", "Text2", "Text3", "Text4"};
//Start work
bw.RunWorkerAsync(param);
}
//Do your Background-Work
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
string[] param = e.Argument as string[];
//Process your long running task
e.Result = null; //Set your Result of the long running task
}
//Taking your results
private void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Apply your Results to your GUI-Elements
myTextBox1.Text = e.Result.ToString();
}
Background-Worker is some old school stuff by the way, so if you like to learn something new take a look here and inform yourself about the TPL. This gives you a better handling of asynchronous.
In fact I think it's not really good to store 4gb data in a XML-File. Do you think about a Database? Or split the XML-File in many XML-Files? So you would be able to read data in chunks.
I hope this helps you.
I don't use background worker for this. I use normal threads instead. Try this code:
public void ButtonDoWork_Click(eventArgs......) {
DoWorkThread = new Thread(new ThreadStart(DoWork)); // Setup thread
DoWorkThread.isBackground = true; // Its background so, we need to set background flag
DoWorkThread.Start(); // Start the thread
}
private Thread DoWorkThread: // our Thread object
private void DoWork() { // This void contains action that will be performed by thread
//TODO: Background processing. To update UI from another thread use Control.Invoke(...)
}
Please note, I don't tested this code - I write it from my memory and it's late so it can not work.
You can also read about Threads at MSDN :)

Multithreaded environment issue

I have a WPF control that is being handle, render and displayed in the main thread of my application. The control uploads thousands of data points into view in an object called "Layer." Here is a rough description of how the object/class hierarchy looks like:
public class WPFControl{
private List<Layer> myLayers;
public List<Layer> MyLayers{
get{ return myLayer;}
}
...
}
public class Layer{
private List<DataPoint> myDataPoints;
public List<DataPoint> MyDataPoints{
get{ return myDataPoints;}
}
...
}
public class DataPoint{
....
}
Since the creation process of this "Layer" object takes some time because of the thousands of DataPoint it has to read and upload, I am creating that layer object in a different thread. That works great and returns the Layer object very nicely. The problem is when I try to do add it to the WPF control to be displayed like this:
myWpfControl.MyLayers.Add(layerCreatedInOtherThread);
the WPF control fires this error:
The calling thread cannot access this object because a different thread owns it
I thought, ok, I can then use the dispatcher like so:
myWpfControl.Dispatcher.Invoke((Action)
(()=>{
myWpfControl.MyLayers.Add(layerCreatedInOtherThread);
})
);
But I keep getting the same error. Any ideas how I can get around this?
Using a BackgroundWorker you can run a task on another thread and then when it is completed have access to the results from the UI thread.
private System.ComponentModel.BackgroundWorker bgWorker;
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
//Start the work
bgWorker.RunWorkerAsync(null) //you can send an argument instead of null
Do the work
private void backgroundWorker1_DoWork(object sender,
DoWorkEventArgs e)
{
// Get the BackgroundWorker that raised this event.
BackgroundWorker worker = sender as BackgroundWorker;
// Assign the result of the computation
// to the Result property of the DoWorkEventArgs
// object. This is will be available to the
// RunWorkerCompleted eventhandler.
e.Result = CreateLayerInOtherThread(); //if you sent an arg instead of null it as availalbe in e.Argument and can be cast from object.
}
Get the result once completed. This runs on the UI thread so you can update it.
private void bgWorker_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
// Next, handle the case where the user canceled
// the operation.
// Note that due to a race condition in
// the DoWork event handler, the Cancelled
// flag may not have been set, even though
// CancelAsync was called.
}
else
{
// Finally, handle the case where the operation
// succeeded.
Layer myLayer = (Layer)e.Result;
myWpfControl.MyLayers.Add(myLayer);
}
}

C# Asynchronous Task for a Stock Ticker

I've been trying to learn more about asynchronous tasks and threading but not making a ton of headway.
I'm trying to load an "Engine" type of thread that will run in the background upon launch and be able to access the UI Thread to update variables, without hanging the UI Thread.
In the below code, Engine is called, and a Ticker object is created which holds the current value of (Litecoin/USD) called Last, also holds several other values that would be useful. This code successfully assigns the current value to label1.text. I don't necessarily need code but what approach would I take to create a ticker object in the background every second and update the UI thread with each new Ticker objects values.
Is this a good case for a background worker?
private void Form1_Load(object sender, EventArgs e)
{
Engine();
}
private void Engine()
{
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
label1.Text = "LTC/USD:" + ltcusd.Last;
}
EDIT:
If I do the following, label1 throws an InvalidOperationException due to a Cross-thread operation attempt (label1 in the UI thread).
private void Form1_Load(object sender, EventArgs e)
{
var t = Task.Factory.StartNew(() => Engine());
t.Start();
}
private void Engine()
{
while (true)
{
Thread.Sleep(1000);
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
label1.Text = "LTC/USD: " + ltcusd.Last;
}
}
Using async/await, the simplest way of getting an "asynchronous" sort of API is to invoke a new task. It's not great, but it'll make things simpler. I would probably create a new class which basically wrapped all the BtceApi methods in tasks:
public class BtceApiAsync
{
public Task<Ticker> GetTickerAsync(BtcePair pair)
{
return Task.Run(() => BtceApi.GetTicker(pair));
}
// etc
}
Then you can use a timer which fires once per second, which will start off a new task and update the UI appropriately:
// Keep a field of type System.Windows.Forms.Timer
timer = new Timer();
timer.Interval = 1000;
timer.Tick += DisplayTicker;
timer.Start();
...
private async void DisplayTicker(object sender, EventArgs e)
{
Ticker ticker = await BtceApiAsync.GetTickerAsync(BtcePair.LtcUsd);
label1.Text = "LTC/USD: " + ltcusd.Last;
}
Note that this doesn't mean the screen will be updated once per second... there will be a new task started once per second, and as soon as each task completes, the UI will be updated.
The use of await here - from an async method started on the UI thread - means you don't need to worry about using the UI; the whole async method will execute on the UI thread, even though the fetch itself happens in a different thread.
You can try ContinueWith to update the Label at the end of the task. If you want to update it event before the task ends then raise an event which is registered by on the UI thread. The event can then update the label.
I suppose this is Windows Forms. You could do it "old school style" and set the label text on the UI thread, and you can do that by passing delegate to the BeginInvoke or Invoke method.
private void Engine()
{
while (true)
{
Thread.Sleep(1000);
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
UpdateText("LTC/USD: " + ltcusd.Last);
}
}
private void UpdateText(string text)
{
//Inspect if the method is executing on background thread
if (InvokeRequired)
{
//we are on background thread, use BeginInvoke to pass delegate to the UI thread
BeginInvoke(new Action(()=>UpdateText(text)));
}
else
{
//we are on UI thread, it's ok to change UI
label1.Text = text;
}
}

How to enable form button after process has exited?

I have an windows application developed using C#. In this application, I am creating one process. I want to enable and disable few buttons when Process_Exited() event occures.
In Process_Exited() method, I have written code to enable buttons but at runtime I get error as
"Cross-thread operation not valid:
Control
'tabPage_buttonStartExtraction'
accessed from a thread other than the
thread it was created on."
My code snippet is :
void rinxProcess_Exited(object sender, EventArgs e)
{
tabPage_buttonStartExtraction.Enabled = true;
tabPageExtraction_StopExtractionBtn.Enabled = false;
}
Can anyone suggest how to make this possible?
Move the enable/disable lines in a separate method and call that method from rinxProcess_Exited using Control.Invoke method.
You're attempting to change the UI from a different thread.
Try something like this;
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
You shouldn't be doing much work on the UI from another thread, as the invocations are quite expensive.
Source: http://msdn.microsoft.com/en-us/library/ms171728.aspx
You must make UI changes on the UI thread. See this question for more details.
Here's the solution applied to your example:
void rinxProcess_Exited(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke((Action)(() => ProcessExited()));
return;
}
ProcessExited();
}
private void ProcessExited()
{
tabPage_buttonStartExtraction.Enabled = true;
tabPageExtraction_StopExtractionBtn.Enabled = false;
}

Categories

Resources