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();
Related
So in the snippet of below I very simply look in a specific folder and copy the images from the source to the destination.
The copy is VERY fast and it works great for the first bunch of folders (maybe 20 or so) which takes a few seconds. But then the progress bar stops moving and I get a spinning mouse cursor. I can look in the destination folder and it is still processing the folders.
When it's done I get the the "Process Complete" dialog box, the progress bar is 100% and everything ran fine.
Just want to make sure the end user doesn't think it's frozen.
private void readInvoices()
{
string InvoiceFile = txtInvoiceFile.Text;
//read in the text file and get all the invoices to copy
string[] Invoices = File.ReadAllLines(InvoiceFile);
//set the max val of the progress bar
progBar.Maximum = Invoices.Length;
try
{
//for every invoice
foreach (string invoice in Invoices)
{
//Set the source and destination directories
string sourceInvFolder = string.Format(#"{0}\{1}", txtSource.Text, invoice);
string destInvFolder = string.Format(#"{0}\{1}", txtDest.Text, invoice);
DirectoryInfo SourceDI = new DirectoryInfo(sourceInvFolder);
DirectoryInfo DestDI = new DirectoryInfo(destInvFolder);
//we know we have it in the CSV but does the directory actually exist?
//if so then let's process
if (Directory.Exists(SourceDI.FullName) == true)
{
//let's copy of the files
CopyAll(SourceDI, DestDI);
RenameFolder(sourceInvFolder);
}
//inc the progress bar
progBar.Increment(1);
}
}
catch (Exception ex)
{
MessageBox.Show("Error" + ex.Message);
}
finally
{
MessageBox.Show("Process Complete");
CleanUp();
}
}
The UI freezes because it's running in a single thread. A workaround to fix the freezing part is putting this line of code inside your loop.
Application.DoEvents();
This code checks if there are messages waiting to be processed, if there are, it processes them before proceeding to another loop. You can use a ProgressBar control to let the user see how much is processed already. If you don't want to stay with the single thread method, use a BackGroundWorker to prevent the form from appearing like it's frozen. This is multithreading, which means a separate thread is processing something while you do something else.
One thing to remember though, using the code above makes the whole looping process go slower since it has to do the checking for every loop, which means more work, in return, you get real-time progress report. The reason it looks frozen is that the loop hasn't finished yet, you'll have to let it finish first before you can do something else because it's running in a single thread.
if you want Just want to make sure the end user doesn't think it's frozen. you should use multihreading . the More suitable class for this task is BackgroundWorker
From MSDN :
The BackgroundWorker class allows you to run an operation on a separate, dedicated thread. Time-consuming operations like downloads and database transactions can cause your user interface (UI) to seem as though it has stopped responding while they are running. When you want a responsive UI and you are faced with long delays associated with such operations, the BackgroundWorker class provides a convenient solution.
try to follow the example provided by msdn and put the call readInvoices() of your method in DoWork event
Your code is running on the UI thread (at least I assume so as you have a MessageBox in your catch block).
So, it will not necessarily process UI updates.
Have a look into doing the work using the TPL.
http://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx
I have the following code:
public class GUI
{
public void threadTask()
{
while(MY_GLOBAL_VARIABLE)
{
// do something
}
}
}
// Execute Thread
GUI gui = new GUI();
Thread t = new Thread(threadTask);
t.Start();
This seems like a messy way to do this. Any better approach how to reference it and kill it instantly? Thanks!
UPDATE: who ever gave me a downvote, LEARN TO READ! I clearly specified what I'm trying to 'kill' in the title, tags and code, at least next time read the post before casting a vote.
What you have is fine, the thread will be cleaned up as soon as it's finished processing.
You don't kill/dispose a thread, it has no IDisposable.
You could put the thread start in a method, so you can call it more then once, when it finished the first time.
You can use:
t.Join();
to catch when the thread finished.
The call to Join() is what de-allocates the thread. You don't have to do anything else. Just make sure that the threads clean up any resources they might be using before they exit.
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.
I have a weird issue:
In my C# app, I am creating another thread, like so:
Thread printThread = new Thread(printWorker);
printThread.Name = "Logger MainThread";
printThread.IsBackground = true;
printThread.Start();
When my main thread finishes, this new thread just keeps on working, although it's marked as Background.
What could be the causes for this?
This object is holding a Mutex object, not sure this may be the reason...
Any ideas anyone?
Here's the code from the printWorker method:
while (loggerIsActive)
{
LogMessage log = LoggerQueue.Dequeue();
if (log.message != null)
{
syncLogObj.WaitOne();
lock (writerobj)
{
StreamWriter sw;
if (!File.Exists(fName))
{
sw = File.CreateText(fName);
}
else
{
sw = new StreamWriter(fName, true);
}
using (sw)
{
if (log.message != "")
{
if (log.message.EndsWith("\r\n"))
{
log.message =
log.message.Substring(0, log.message.Length - 2);
}
sw.WriteLine(string.Format("[{0}][{3}][{1}] | {2}",
log.msgTime,
log.level.ToString(),
log.message,
log.sender.ToString()));
}
sw.Flush();
sw.Close();
}
}
syncLogObj.ReleaseMutex();
}
Thread.Sleep(5);
}
Try this:
Start the app through VS and exit normally. The VS should stay in Debug mode as you described. Click on Pause button (Break all) and then go to Debug->Windows->Threads. Do you see your "Logger MainThread" in the list?
If so, double-click it, it should lead you to the code line that the thread is currently executing. Step-debug from there and see why is it not terminating.
If you don't see it try looking at other threads that have not terminated and try to find the problem.
Otherwise, with those kind of problems it's always useful to monitor the program state via System.Diagnostics.Debug.Print statements (you can see them printing in the VS output window).
kill it.
Not pretty. But this isn't TV. Read on:
1) Not sure you use are using it but it appears you should be locking loggerqueue before you queue(main pgm) or dequeue(thread).
2) No need to lock writerobj with just this setting. But really you should so you can safely kill the thread not during a write:
main thread:
do everything
before close:
-lock writerobj
-printthread.abort
worker thread:
add try catch to handle threadabort exception and just quit
If you're properly doing this, you shouldn't have to use Waits and mutexes. If you are using wait properly anyway you won't need the sleep.
General advice for this application: why not log on main thread? if your logging is that busy, log results will be pretty useless.
But there are rare cases where that might be wrong. Entonces......
General advice to have threads play nice for this problem:
Main program
encapsulate logging (notably, quit flag, queue, and worker thread ref) in an object
'global snobs?' Logging is a rare excuse to use singleton patter.
start worker thread in logger object via method
main thread always calls a single method on logger object to log error
That method locks the queue and adds to it.
Use Monitor/Pulse/Wait, no sleep; full examples abound; it is worth learning
because only this thread is hitting the file anyway, unless you have multiple processes, you don't need waitone/releasemutex.
That logging method monitor.pulses an object
That frees the worker thread's monitor.wait (which is what idles the CPU instead of sleep)
lock the queue, only inside the lock dequeue the object to local ref; nothing else.
Do your normal logging code and 'exit check' loop. Add
Your logic code could leave message unwritten if queue is full on quit:
change to exit check so you can do it without an extra lock of queue:
move declaration of queued object refernce above while; set it to nothing
change logic in while to 'loggerisactive or log != null'
when your main thread finishes, in your exit code:
set the quit flag
pulse the object you're using to wait incase it's not processing the queue
Thread will fall thru.
You have a lot of stuff going on that you're obviously not showing...
Exmaple: you have syncLogObj.WaitOne();, but we don't see where syncLogObj is being declared, or used elsewhere in your program.
Plus, you don't need it... get rid of the syncLogObj thing altogether (including the "ReleaseMutex" garbage)... you already have a lock (blah) { }, and that's all you need (from what code you have displayed).
It's likely that the main thread is NOT ending, likely because of this or some other object that is keeping it open.
So, simple instructions
Get rid of syncLogObj (because you already have the "lock")
Make sure you set loggerIsActive = false somewhere.
Edit: Even more details!
From what I see - you don't need the lock (writerobj) at all, because (I'm quite sure), you only seem to have one thread that is writing to the log.
The "lock" is only there if you have two or more threads that running that code (basically).
If printworker does not finish before your main thread is done, then main will die and your printworker thread will be killed by the OS. If you want main to wait for the thread you created, then you should call printThread.Join() in main. That will get main to wait on your thread.
When main finishes your program dies and your printThread will be destroyed by the OS, It will not keep running.
From here
Background threads are identical to
foreground threads with one exception:
a background thread does not keep the
managed execution environment running.
Once all foreground threads have been
stopped in a managed process (where
the .exe file is a managed assembly),
the system stops all background
threads and shuts down.
Tony the Tiger has the right idea but additional code needs to be added to kill the thread before the application closes.
printThread.Join(1000);
if(printThread!=null && printThread.IsAlive)
printThread.Abort();
Thread.Abort();
Thread.Dispose();
That should do it if I'm not mistaken.
this kind of follows on from another question of mine.
Basically, once I have the code to access the file (will review the answers there in a minute) what would be the best way to test it?
I am thinking of creating a method which just spawns lots of BackgroundWorker's or something and tells them all load/save the file, and test with varying file/object sizes. Then, get a response back from the threads to see if it failed/succeeded/made the world implode etc.
Can you guys offer any suggestions on the best way to approach this? As I said before, this is all kinda new to me :)
Edit
Following ajmastrean's post:
I am using a console app to test with Debug.Asserts :)
Update
I originally rolled with using BackgroundWorker to deal with the threading (since I am used to that from Windows dev) I soon realised that when I was performing tests where multiple ops (threads) needed to complete before continuing, I realised it was going to be a bit of a hack to get it to do this.
I then followed up on ajmastrean's post and realised I should really be using the Thread class for working with concurrent operations. I will now refactor using this method (albeit a different approach).
In .NET, ThreadPool threads won't return without setting up ManualResetEvents or AutoResetEvents. I find these overkill for a quick test method (not to mention kind of complicated to create, set, and manage). Background worker is a also a bit complex with the callbacks and such.
Something I have found that works is
Create an array of threads.
Setup the ThreadStart method of each thread.
Start each thread.
Join on all threads (blocks the current thread until all other threads complete or abort)
public static void MultiThreadedTest()
{
Thread[] threads = new Thread[count];
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new Thread(DoSomeWork());
}
foreach(Thread thread in threads)
{
thread.Start();
}
foreach(Thread thread in threads)
{
thread.Join();
}
}
#ajmastrean, since unit test result must be predictable we need to synchronize threads somehow. I can't see a simple way to do it without using events.
I found that ThreadPool.QueueUserWorkItem gives me an easy way to test such use cases
ThreadPool.QueueUserWorkItem(x => {
File.Open(fileName, FileMode.Open);
event1.Set(); // Start 2nd tread;
event2.WaitOne(); // Blocking the file;
});
ThreadPool.QueueUserWorkItem(x => {
try
{
event1.WaitOne(); // Waiting until 1st thread open file
File.Delete(fileName); // Simulating conflict
}
catch (IOException e)
{
Debug.Write("File access denied");
}
});
Your idea should work fine. Basically you just want to spawn a bunch of threads, and make sure the ones writing the file take long enough to do it to actually make the readers wait. If all of your threads return without error, and without blocking forever, then the test succeeds.