I have the need to generate dynamically generate a FlowDocument from a large set of data. Because the process takes several minutes, I'd like to perform the operation on a background thread rather than have the UI hang.
However, I can't generate the FlowDocument on a non-UI thread otherwise attempts to insert rectangles and images cause run-time errors complaining that it's not STA thread.
There are a couple of threads on StackOverflow which seem to involve the same problem I'm having:
Accessing a WPF FlowDocument in a BackGround Process
WPF : Is it impossible to UI load in background thread?
In the first link someone suggests the following:
"What I'd do: use a XamlWriter and serialize the FlowDocument into an XDocument. The serialization task involves the Dispatcher, but once it's done, you can run as many wacky parallel analyses of the data as you want and nothing in the UI will affect it. (Also once it's an XDocument you query it with XPath, which is a pretty good hammer, so long as your problems are actually nails.)"
Can someone elaborate on what does the author mean by this?
For any future visitors
I faced the same problem and solved all thanks to this article
article
What ended up doing is creating the object on a background thread
Thread loadingThread = new Thread(() =>
{
//Load the data
var documant = LoadReport(ReportTypes.LoadOffer, model, pageWidth);
MemoryStream stream = new MemoryStream();
//Write the object in the memory stream
XamlWriter.Save(documant, stream);
//Move to the UI thread
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(Action<MemoryStream>)FinishedGenerating,
stream);
});
// set the apartment state
loadingThread.SetApartmentState(ApartmentState.STA);
// make the thread a background thread
loadingThread.IsBackground = true;
// start the thread
loadingThread.Start();
And then wrote the result in the memory stream as xaml so we can read it back in the main thread
void FinishedGenerating(MemoryStream stream)
{
//Read the data from the memory steam
stream.Seek(0, SeekOrigin.Begin);
FlowDocument result = (FlowDocument)XamlReader.Load(stream);
FlowDocumentScrollViewer = new FlowDocumentScrollViewer
{
Document = result
};
//your code...
Wish it could save others some time :)
While not realy an answer to elaborating on what the author of your quote means, maybe this can be a solution for your problem:
If you hook yourself into the Application.Idle Event, you can build your FlowDocument one by one there. This event still is in the UI Thread so you won't get problems like in a background worker.
Altho you have to be carefull not to do too much work at once, otherwise you will block your application.
If it is possible to seperate your generation process into small chunks you can process those chunks one by one in this event.
Related
I have a COM object with a GUI-component, lets call it "FileViewer". This FileViewer component can load a file by using a method, lets call it "LoadFile", that needs to use the UI thread to work. To use LoadFile, the FileViewer have to already been added and loaded into the GUI.
The problem is that the files that you load with LoadFile can be really big which causes the application UI to freeze for multiple seconds, making for a terrible user experience.
My quesion is: Is there anyway to call this LoadFile async without locking the UI thread even thought it requiring a UI thread to load.
What I've tried:
//My standard approach of doing things async
public async btnPressed (){
await new TaskFactory().StartNew(() => {
myFileViewer.LoadFile("C:\..."); //This still freezes the UI thread
});
}
//Maybe could prevent locking if it's not visible
myFileViewer.Parent = null;
myFileViewer.Visible = false;
myFileViewer.LoadFile("C:\..."); //This still freezes the UI
myFileViewer.Parent = this;
myFileViewer.Visible = true;
//Maybe could create the viewer in a new window "TestProxy" with a new "UI thread", load the file and then insert the viewer back into my first window
Thread thread = new Thread(() => {
new TestProxy().Show();
TestProxy.CreateViewerAndLoadFile("C:\..."); //Does actually not freeze my application
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
//this version actually worked, but the problem is that there seem to be no way to move the viewer back into my original window after loading it into this new one
I'm very sure that the Viewer class in itself does not have any support for doing LoadFile async, so I am more interested in a general solution of the problem. Just dosn't feel like it should be something that's impossible to do.
PS:
The name of the GUI component is "AxInventorViewControl" which I create an instance of and "LoadFile" is infact a row of code axInventorViewControl1.FileName = filePath;
So I am trying to use threads to save a file in the background to prevent the main unity thread from freezing until the file is saved:
public void SaveMap() {
MapRendererBehaviour mapRendererBehaviour = GameObject.FindWithTag("MapRenderer").GetComponent<MapRendererBehaviour>();
SerializedMap serializedMap = mapRendererBehaviour.ToSerializedData();
Debug.Log("test");
_saveMapThread = new Thread(() => {
string saveMapJson = JsonConvert.SerializeObject(serializedMap);
File.WriteAllText(_saveMapFilePath, saveMapJson);
});
}
For some reason the thread code does not seem to be executing because the file is not getting saved. If I comment out the thread creation and just leave the body of the thread in there, the file saves properly.
Anyone know what I might be doing wrong here (I assume I am doing something dumb as I have never tried to use threads before).
This might be a shot in the dark but if you are using named threads instead of tasks you normally have to say ThreadName.Start() after so for example after declaring the thread say:
_saveMapThread.Start();
I have a question about solving UI freezing.
Introduction:
I am currently programming an OPC based online alarm reader for a given analyzer tool. This tool received data from an excel sheet, analyzed this data using a rule base and a topology model and shows the results with TreeViewItems.
My task is to replace the excel sheet reader by a real time alarm reader.
That's done, I can connect my software to server and receive data packages every time new alarms are created.
Problem:
My solution for transport the new data to the main class and from there to the analyzer class is saving the data in a list, adding them to an EventArgs and raising an Event. The handling method in the main class receives this data, starts a new task (Task>) for the analyzer and returns its results to the Main Thread.
This construction should have uncoupled the calculation process from the UI The analyzing process takes about 1.3s with example data. New data arrives every 2 seconds on average. 2 seconds is in the same time the highest refreshing time.
Attached is a code snippet from the handling method
Task<List<Analyser.ALARM_GROUP>> analysertask = Task.Factory.StartNew<List<Analyser.ALARM_GROUP>>(
()=>
{
/*if the alarmlist is emty, set the new alarms as alarmlist,
* else add the new alarms to the present list*/
if (AlarmList1.Count == 0)
AlarmList1 = e.CurrentAlarms;
else listModifier.mergeList(e.CurrentAlarms, AlarmList1);
/*Start the analysis process in a seperate task and return the Alarm_Group-List*/
return Analyser1.startAnalysis(PlantModelReader1, AlarmlogReader1, RuleBaseLoader1, AlarmList1);
});
Cursor = Cursors.Wait;
List<Analyser.ALARM_GROUP> alarmGroupList = analysertask.Result;
showAlarmLog(alarmGroupList);
Cursor = Cursors.Arrow;
Unfortunately the UI still is stuck when I start analyzing process and I don't even know if my concept of starting a new thread every two seconds(average appearance time of new alarms) is a reasonable concept.
I guess the problem is inside showAlarmLog but it is a lot of code. If needed I will post this code, too.
I would be thankful for any suggestions on this problem, even a "your concept is crap, try this idea: ..." would be good to know.
Kind regards
Larimow
The problem is that this call:
List<Analyser.ALARM_GROUP> alarmGroupList = analysertask.Result;
Blocks the UI thread until the background task is completed.
The way to handle this is to use a continuation task instead of waiting on the result:
Task<List<Analyser.ALARM_GROUP>> analysertask = Task.Factory.StartNew<List<Analyser.ALARM_GROUP>>(
()=> {
// Keep existing code
return Analyser1.startAnalysis(PlantModelReader1, AlarmlogReader1, RuleBaseLoader1, AlarmList1);
});
// Make a continuation here...
analysertask.ContinueWith( t =>
{
Cursor = Cursors.Wait;
List<Analyser.ALARM_GROUP> alarmGroupList = t.Result;
showAlarmLog(alarmGroupList);
Cursor = Cursors.Arrow;
}, TaskScheduler.FromCurrentSynchronizationContext());
By scheduling this as a continuation, it will run when the first task completes. By using TaskScheduler.FromCurrentSynchronizationContext, you say to marshal this back onto the UI thread when it executes.
Note that this becomes far easier with .NET 4.5/C# 5. With C# 5, you could write this as:
var analysertask = Task.Run(
()=> {
// Keep existing code
return Analyser1.startAnalysis(PlantModelReader1, AlarmlogReader1, RuleBaseLoader1, AlarmList1);
});
Cursor = Cursors.Wait;
List<Analyser.ALARM_GROUP> alarmGroupList = await analysertask;
showAlarmLog(alarmGroupList);
Cursor = Cursors.Arrow;
This requires the method to be, itself, flagged with the new async keyword, however.
First of all, the code below seems to be working.
It extracts jpeg images from a continuous byte stream and displays them in a pictureBox as they arrive if the encapsulating packet checksum is correct.
The concern is intermittent GUI problems since the pictureBox is asynchronously updated by RxThread.
Is the method used here OK or might this crash while showing it to the customer?
public FormMain()
{
InitializeComponent();
var t1 = new Thread(RxThread) { IsBackground = true };
t1.Start();
}
private void RxThread()
{
while (true)
{
... // validate incoming stream
var payload = new Byte[payloadSize];
... // copy jpeg image from stream to payload
pictureBox.Image = new Bitmap(new MemoryStream(payload));
}
}
I think all access to UI controls should be done from UI thread. Modifying control from the thread that doesn't own the underlying handle may have undesirable effects. In the best case scenario the exception will be thrown, in the worst case everything may seem to be all right until some race condition happens (and you may spend lots of time trying to replicate it).
Use Invoke method, passing your delegate that will be executed on the UI thread.
Why you don't use Invoke to update the PictureBox?
Are you sure that even works at all? I don't see why it wouldn't raise a InvalidOperationException: (Cross-thread operation not valid) as the control is being updated from a thread other than the one which is was created on. You should update the UI via a delegate method that is invoked on the UI thread.
I have to be able to save a file, unfortunatly it can potentially be very large so saving it can potentially take minutes. As I need to do this from a GUI thread I don't want to block the GUI from executing. I was thinking about attempting the save operation on a seperate thread to allow the primary GUI thread to continue executing.
Is there a nice (easy) way to spawn a new thread, save the file, and destroy the thread without any nasty side effects?!
It must be said that I have NEVER had to use threads before so I am a complete novice! Any and all help would be greatly appreciated!
BackgroundWorker (as suggested by Frederik) is a good choice, particularly if you want to report progress to the UI while you're saving. A search for BackgroundWorker tutorial gets a lot of hits, so you should be able to follow one of those to get you started.
One thing to be careful of: would there be any way of changing the data structure that you'll be trying to save from the UI thread? If so, you should disable those aspects of the UI while you're saving - it would (probably!) be bad to be half way through saving the data, then allow the user to change some of it. If you can get away with effectively handing off the data to the background thread and then not touching it from the UI thread, that will make your life a lot easier.
You could maybe use the BackGroundWorker component, as it will abstract a bit the Threading part for you.
Your problem might be that there are several nice and easy ways of doing it. If you just want to set off the file save and not worry about knowing when it has completed, then having a method
void SaveMyFile(object state)
{
// SaveTheFile
}
and calling it with
ThreadPool.QueueUserWorkItem( SaveMyFile );
will do what you want.
I would recommend doing Asynchronous I/O. It's a little bit easier to set up and doesn't require you to create new threads yourself.
Asynchronous programming is where you have, for example, a file stream you want to write to but does not want to wait for it to finish. You might want to be notified when it's finished but you don't want to wait.
What you do is using the BeginWrite/BeginRead and EndWrite/EndRead functions that are available on the Stream class.
In your method you start by calling BeginWrite with all the data you want to write and also pass in a callback function. This function will be called when BeginWrite has finished.
Inside the callback function you call EndWrite and clean up the stream and check for errors.
BeginWrite will not block which means that if it's called from within an event handler that thread can finish that handler and continue processing more event (such as other GUI events).
using System;
using System.IO;
using System.Text;
class Program
{
private static FileStream stream;
static void Main(string[] args)
{
stream = new FileStream("foo.txt",
FileMode.Create,
FileAccess.Write);
const string mystring = "Foobarlalala";
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] data = encoding.GetBytes(mystring);
Console.WriteLine("Started writing");
stream.BeginWrite(data, 0, data.Length, callback, null);
Console.WriteLine("Writing dispatched, sleeping 5 secs");
System.Threading.Thread.Sleep(5000);
}
public static void callback(IAsyncResult ia)
{
stream.EndWrite(ia);
Console.WriteLine("Finished writing");
}
}
}
The sleeping is pretty important because the thread that's writing stuff will be killed if the main thread is killed off. This is not an issue in a GUI application, only here in this small example.
MSDN has a pretty good overview on how to write this stuff, and also some good articles on Asynch programming in general in case you go for the backgroundworker or ThreadPool.
or u could use old friends delegates.