Synchronous/Blocking Application.Invoke() for GTK# - c#

Unfortunately, Application.Invoke() is asynchronous:
private string ThreadFunction(int i)
{
string result = null;
Gtk.Application.Invoke(delegate
{
OutputStringToUserInterface("i = " + i.ToString());
result = GetStringFromUserInterface();
});
return result;
}
This means that in this example ThreadFunction() proceeds immediately after calling Application.Invoke(), resulting in an probably undefined state of the result string. -- Typically ThreadFunction() will be faster and will return with the old value (i.e. null).
This is a workaround using ManualResetEvent to make Application.Invoke() synchronous:
private string ThreadFunction(int i)
{
string result = null;
using (var ev = new ManualResetEvent(false))
{
Gtk.Application.Invoke(delegate
{
OutputStringToUserInterface("i = " + i.ToString());
result = GetStringFromUserInterface();
ev.Set();
});
ev.WaitOne();
}
return result;
}
This way, ThreadFunction() waits until Application.Invoke() returns, like it would do using WinForms Control.Invoke().
EDIT: Better example code
EDIT2: Add missing using
Now my question: Is there a better solution?

Well, yes, there's no reason to wait for the delegate to execute to get the proper return value. Fix:
int result = i + 1;
And it's okay to let the OutputStringToUserInterface() execute asynchronously, assuming you don't call ThreadFunction() so often that it floods the UI thread with requests.
If your real code actually depends on a return value from a function that must run on the UI thread then, no, you can't make it faster. Clearly, that's something you really want to avoid.

You can encapsulate your current code into a generic wrapper:
public static void GuiInvoke(Action action)
{
var waitHandle = new ManualResetEventSlim();
Gtk.Application.Invoke( (s,a) =>
{
action();
waitHandle.Set();
});
waitHandle.Wait();
}
public static void BeginGuiInvoke(Action action)
{
Gtk.Application.Invoke( (s,a) => {action();});
}

Related

How to make a pause in a procedure and then return value after it?

I'm working on a C# project, want to make a small pause about 2 seconds inside a procedure.
Actually I have tried to use Invoke, but as you know, we can't use it inside a class this kind of procedure.
Here is my code for more details:
public class GenerateFile
{
public CSPFEnumration.ProcedureResult GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
{
string script = string.Format(" DECLARE #RC INT " +
" DECLARE #Daftar_No INT = '{0}' " +
" DECLARE #hokm_type_code INT = 100 " +
" DECLARE #Channelno INT = '{1}' " +
" DECLARE #Id_No BIGINT = '{2}' " +
" EXEC #rc = [dbo].[Hokm_with_type] #Daftar_No, #hokm_type_code, #Channelno, #Id_No ",
Daftar_No,
Channelno,
NationalCode);
try
{
IEnumerable<string> commandStrings = Regex.Split(script, #"^\s*GO\s*$",
RegexOptions.Multiline | RegexOptions.IgnoreCase);
Connect();
foreach (string commandString in commandStrings)
{
if (commandString.Trim() != "")
{
using (var command = new SqlCommand(commandString, Connection))
{
command.ExecuteNonQuery();
}
}
}
DisConnect();
string FaxFilePath = InternalConstant.FaxFilePath + "\\" + string.Format("Lhokm{0}.tif", Channelno);
// I want to make a pause in here without locking UI
if (File.Exists(FaxFilePath))
return CSPFEnumration.ProcedureResult.Success;
else
return CSPFEnumration.ProcedureResult.Error;
}
catch (Exception ex)
{
InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);
return CSPFEnumration.ProcedureResult.Error;
}
}
}
I have tried await too, but I cant return a proper value. because in this procedure if I use await, the value will return before finishing await.
Edit:
And also I dont want to use Thread.Sleep because it will lock UI.
Thanks for any helping.
Use async await feature :
Mark your method as async .
Add Task.Delay(2000) as the waited task.
public async CSPFEnumration.ProcedureResult GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
{
-----
// I want to make a pause in here without locking UI
await Task.Delay(2000);
-----
}
Asking for downvotes:
DoEvents
Warning: Total, Complete and Inexcusably Flagrant Barnyard Programming:
// before call (disable the UI element that called this so it can't re-enter)
DateTime st = DateTime.Now();
while(DateTime.Now.Subtract(st).TotalSeconds<3)
System.Windows.Forms.DoEvents();
// after call (re-enable UI element)
This will appear to work. No responsibility if people point and laugh.
Hey, you asked!
You can look around Task.Delay() it will not block current thread and continue execution after number of milliseconds.
Exmaple usage from msdn:
Stopwatch sw = Stopwatch.StartNew();
var delay = Task.Delay(1000).ContinueWith(_ =>
{ sw.Stop();
return sw.ElapsedMilliseconds; } );
Console.WriteLine("Elapsed milliseconds: {0}", delay.Result);
// The example displays output like the following:
// Elapsed milliseconds: 1013
Or maybe look around Timer class.
I can see it working with events or Tasks (if you cannot use async / await). This is how to create event. We can use separate Thread to check if file is created and fire event if it is:
public class FileGenEventArgs : EventArgs
{
public string ProcedureResult { get; set; }
}
public class GenerateFile
{
public event EventHandler<FileGenEventArgs > fileCreated;
public GenerateFile()
{
// subscribe for this event somewhere in your code.
fileCreated += GenerateFile_fileCreated;
}
void GenerateFile_fileCreated(object sender, FileGenEventArgs args)
{
// .. do something with args.ProcedureResult
}
private void FileCheck()
{
Thread.Sleep(2000); // delay
fileCreated(this, new FileGenEventArgs()
{
ProcedureResult = File.Exists(FaxFilePath) ?
CSPFEnumration.ProcedureResult.Success :
CSPFEnumration.ProcedureResult.Error
});
}
public void GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
{
try
{
// this .Sleep() represents your sql operation so change it
Thread.Sleep(1000);
new Thread(FileCheck).Start();
}
catch (Exception ex)
{
InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);
}
}
}
Pros :
Pause that you wanted
Doesn't block the UI thread.
Event-based approach (which is proper way of dealing with this kind of problems)
Cons :
Requires to refactor your code
The most easy thing to wait while keeping the UI responsive is using async-await.
To do this, you must declare your function async, and return Task instead of void and Task<TResult> instead of TResult:
public async Task<CSPFEnumration.ProcedureResult> GenerateFaxFile(
string Daftar_No,
string Channelno,
string NationalCode)
{
// do your stuff,
}
Now whenever you do something that takes some time, use the async version of the function to start the process. While this process is running, you can do other stuff. When you need the result await for the task, and you get the void if the async returns Task, or the TResult if the async returns Task<TResult>
public async Task<CSPFEnumration.ProcedureResult> GenerateFaxFile(
string Daftar_No,
string Channelno,
string NationalCode)
{
IEnumerable<string> commandStrings = Regex.Split(
script, #"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase);
Connect();
foreach (var commandString in commandStrings)
{
if (commandString.Trim() != "")
{
using (var command = new SqlCommand(commandString, Connection))
{
Task<int> task = command.ExecuteNonQueryAsync();
// while the command is being executed
// you can do other things.
// when you need the result: await
int result = await task;
// if useful: interpret result;
}
}
}
DisConnect();
... etc.
}
Every function that calls an async function should be declared async
every async function returns Task instead of void and Task<TResult> instead of TResult
There is only one exception: the event handler may return void.
Example of async event handler:
private async void OnButton1_Clicked(object sender, ...)
{
var task = GenerateFaxFile(...);
// while the fax file is generated do some other stuff
// when you need the result:
var procedureResult = await task;
Process(procedureResult);
}
Note that everything is processed by the UI thread. The only difference is that as soon as anything time consuming happens, the process doesn't have a busy wait, but processes UI input.
The above is enough to keep your UI responsive. You said you wanted to know how to wait some time. From the rest of your question I understand that you meant: how to interrupt the procedure while it is waiting for something, so the UI can do other thing. If you really need to wait some time while keeping the UI responsive, use Task.Delay(TimeSpan).
Eric Lippert (thanx Eric!) explained async-await as follows in Stackoverflow - async/await - Is this understanding correct?
Suppose for breakfast you have to toast bread and cook eggs. There are several scenarios for it:
Start toasting bread. Wait until it is finished. Start cooking eggs, wait until it is finished. Synchronous processing. While you are waiting for the bread to toast you can't do anything else.
Start toasting bread, while the bread is being toasted start cooking eggs. when the eggs are cooked wait until the bread finished toasting. This is called Asynchronous, but not concurrent. It is done by the main thread and as long as this thread does something, the main thread can't do anything else. But while it is waiting it has time to do other things (make some tea for instance)
Hire cooks to toast the bread and cook the eggs. Wait until both are finished. Asynchronous and concurrent: the work is done by different threads. This is the most expensive because you have to start new threads.
Finally a note about your exception handling
Do you notice that if an exception occurs you don't disconnect?. The proper way to make sure that disconnect is always called is the following:
try
{
Connect();
... do other stuff
}
catch (Exception exc)
{
... process exception
}
finally
{
Disconnect();
}
The finally part is always executed, regardless of any exception being thrown or not.
You can use simple Thread Pool to archive this. However your return has to do asynchronously so it doesn't lockup the gui.
public void GenerateFaxFile(string Daftar_No, string Channelno,
string NationalCode, Action<CSPFEnumration.ProcedureResult> result)
{
ThreadPool.QueueUserWorkItem(o =>
{
string script = "your script";
try
{
// more of your script
// I want to make a pause in here without locking UI
while (true)
{
// do your check here to unpause
if (stopMe == true)
{
break;
}
Thread.Sleep(500);
}
if (File.Exists(FaxFilePath))
{
result(CSPFEnumration.ProcedureResult.Success);
return;
}
else
{
result(CSPFEnumration.ProcedureResult.Error);
}
}
catch (Exception ex)
{
InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);
result(CSPFEnumration.ProcedureResult.Error);
return;
}
});
}
public void HowToUseMe()
{
GenerateFaxFile("", "", "", result => {
if (result == CSPFEnumration.ProcedureResult.Error)
{
// no good
}
else
{
// bonus time
}
});
}
You should use the old good background thread (see answer written by FabJan) or you can use async and await with synchronization context:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void buttonStart_Click(object sender, EventArgs e)
{
await progressBar1.DoProgress(2000);
Trace.WriteLine("Done");
MessageBox.Show("Done");
}
private void buttonMoveButton1_Click(object sender, EventArgs e)
{
//to prove UI click several times buttonMove while the task is ruunning
buttonStart.Top += 10;
}
}
public static class WaitExtensions
{
public static async Task DoProgress(this ProgressBar progressBar, int sleepTimeMiliseconds)
{
int sleepInterval = 50;
int progressSteps = sleepTimeMiliseconds / sleepInterval; //every 50ms feedback
progressBar.Maximum = progressSteps;
SynchronizationContext synchronizationContext = SynchronizationContext.Current;
await Task.Run(() =>
{
synchronizationContext.OperationStarted();
for (int i = 0; i <= progressSteps; i++)
{
Thread.Sleep(sleepInterval);
synchronizationContext.Post(new SendOrPostCallback(o =>
{
Trace.WriteLine((int)o + "%");
progressBar.Value = (int)o;
}), i);
}
synchronizationContext.OperationCompleted();
});
}
}
It could appear that MessageBox done Shows before the ProgressBar is on its Maximum. I blame for this magic animation of progressBar in Windows 8. Please correct me if I am wrong.

Convert any given function into an awaitable task

The goal of the following code is to cast any given function into an awaitable function. The idea is to use it when fetching the data from the db, giving the code the flexibility to either use the synchronous fetch functions (an imposition of my current ORM), or use the very same functions as async.
I am aware that there could be many things wrong with the concept behind code. By now I was just trying to get rid of the compiler errors so I can run the code and check the behavior. But of course I am open to discuss the concept beforehand, and if the whole idea behind it is wrong then use my time more efficiently looking for another solution.
async static void Main()
{
// The following line gives a compiler error:
// Error 1 The best overloaded method match for 'CastFuncToTask<int>(System.Func<int>)' has some invalid arguments
int task = await CastFuncToTask<int>(TestFunc(2));
}
private static Task<T> CastFuncToTask<T>(Func<T> func)
{
TaskCompletionSource<T> taskCompletionSource = new TaskCompletionSource<T>();
T result = func.Invoke();
taskCompletionSource.SetResult(result);
return taskCompletionSource.Task;
}
private static int TestFunc(int testInt)
{
return testInt * 2;
}
Running .NET 4.5, you can greatly simplify your code by doing:
int task = await Task.FromResult(TestFunc(2));
No need to wrap it yourself in a TaskCompletionSource.
I am aware that there could be many things wrong with the concept
behind code.
If what you're trying to do is asynchronously query your database, this solution will definitely not help. It only artificially wraps your result in a Task. If you really want to query your database asynchronously, you need to use async methods provided by your database provider.
If you're using MySQL and looking for a driver that supports async, look into Dapper
You can use:
await Task.Run(() => obj.functionname());
change it to
int task = await CastFuncToTask<int>(()=>TestFunc(2));
In your code the input provided for CastFuncToTask is an int(what TestFunc returns.) but it needs a delegate Func<T>.
my method is this :
public Task<int> SetOffAsync()
{
return Task<int>.Factory.StartNew(() =>
{
/*do something else*/
return 42;
});
}
and you can call this :
int var = await SetOffAsync();
With a windows forms application I confirmed that the first call is blocking the second isn't. So the best solution would be to use Task.Run() with a lambda expression.
private async void button1_Click(object sender, EventArgs e)
{
button1.Text = "...";
var result = await Task.FromResult(TestFunc(2));
button1.Text = result.ToString();
}
private async void button2_Click(object sender, EventArgs e)
{
button2.Text = "...";
var result = await Task.Run(() => TestFunc(2));
button2.Text = result.ToString();
}
private int TestFunc(int x)
{
System.Threading.Thread.Sleep(5000);
return x;
}
The compiler can not know that you don't want to evaluate TestFunc(2) but use it as a delegate and will just execute the method first and Task.FromResult will just wrap a return value in a Task. This is not what you want.
Below code may help others:
Note: GenericMethod is parameter name which we are passing as Func with Input (T1) and output (T2) of generic Type.
Task Factory will provide necessary access to run your task.
public async Task<T2> GenericAsyncCall<T1, T2>(Func<T1, T2> GenericMethod, T1 input)
{
var _output = await Task.Factory.StartNew(() => {
var output = GenericMethod.Invoke(input);
return output;
});
return (T2)_output;
}
The problem is that CastFuncToTask runs synchronously.
if Func is parameterless you can do something like this:
public static Task<T> ToTask(this Func<T> func) => Task.Factory.StartNew(() => func());
Anyway we can play with TaskCompletionSource and return something we want regardless of the method. Lets say we have Action instead of Func
public static Task<string> ToTask(this Action action)
{ TaskCompletionSource<string> taskCompletionSource = new
TaskCompletionSource<string>();
string result = null;
try
{
Task.Factory.StartNew(() => action());
}
catch (Exception ex)
{
result = ex.Message;
}
taskCompletionSource.SetResult(result);
return taskCompletionSource.Task;
}

Thread safe with Linq and Tasks on a Collection

Given some code like so
public class CustomCollectionClass : Collection<CustomData> {}
public class CustomData
{
string name;
bool finished;
string result;
}
public async Task DoWorkInParallel(CustomCollectionClass collection)
{
// collection can be retrieved from a DB, may not exist.
if (collection == null)
{
collection = new CustomCollectionClass();
foreach (var data in myData)
{
collection.Add(new CustomData()
{
name = data.Name;
});
}
}
// This part doesn't feel safe. Not sure what to do here.
var processTasks = myData.Select(o =>
this.DoWorkOnItemInCollection(collection.Single(d => d.name = o.Name))).ToArray();
await Task.WhenAll(processTasks);
await SaveModifedCollection(collection);
}
public async Task DoWorkOnItemInCollection(CustomData data)
{
await DoABunchOfWorkElsewhere();
// This doesn't feel safe either. Lock here?
data.finished = true;
data.result = "Parallel";
}
As I noted in a couple comments inline, it doesn't feel safe for me to do the above, but I'm not sure. I do have a collection of elements that I'd like to assign a unique element to each parallel task and have those tasks be able to modify that single element of the collection based on what work is done. End result being, I wanted to save the collection after individual, different elements have been modified in parallel. If this isn't a safe way to do it, how best would I go about this?
Your code is the right way to do this, assuming starting DoABunchOfWorkElsewhere() multiple times is itself safe.
You don't need to worry about your LINQ query, because it doesn't actually run in parallel. All it does is to invoke DoWorkOnItemInCollection() multiple times. Those invocations may work in parallel (or not, depending on your synchronization context and the implementation of DoABunchOfWorkElsewhere()), but the code you showed is safe.
Your above code should work without issue. You are passing off one item to each worker thread. I'm not so sure about the async attribute. You might just return a Task, and then in your method do:
public Task DoWorkOnItemInCollection(CustomData data)
{
return Task.Run(() => {
DoABunchOfWorkElsewhere().Wait();
data.finished = true;
data.result = "Parallel";
});
}
You might want to be careful, with large amount of items, you could overflow your max thread count with background threads. In this case, c# just deletes your threads, which can be difficult to debug later.
I have done this before, It might be easier if instead of handing the whole collection to some magic linq, rather do a classic consumer problem:
class ParallelWorker<T>
{
private Action<T> Action;
private Queue<T> Queue = new Queue<T>();
private object QueueLock = new object();
private void DoWork()
{
while(true)
{
T item;
lock(this.QueueLock)
{
if(this.Queue.Count == 0) return; //exit thread
item = this.Queue.DeQueue();
}
try { this.Action(item); }
catch { /*...*/ }
}
}
public void DoParallelWork(IEnumerable<T> items, int maxDegreesOfParallelism, Action<T> action)
{
this.Action = action;
this.Queue.Clear();
this.Queue.AddRange(items);
List<Thread> threads = new List<Thread>();
for(int i = 0; i < items; i++)
{
ParameterizedThreadStart threadStart = new ParameterizedThreadStart(DoWork);
Thread thread = new Thread(threadStart);
thread.Start();
threads.Add(thread);
}
foreach(Thread thread in threads)
{
thread.Join();
}
}
}
This was done IDE free, so there may be typos.
I'm going to make the suggestion that you use Microsoft's Reactive Framework (NuGet "Rx-Main") to do this task.
Here's the code:
public void DoWorkInParallel(CustomCollectionClass collection)
{
var query =
from x in collection.ToObservable()
from r in Observable.FromAsync(() => DoWorkOnItemInCollection(x))
select x;
query.Subscribe(x => { }, ex => { }, async () =>
{
await SaveModifedCollection(collection);
});
}
Done. That's it. Nothing more.
I have to say though, that when I tried to get your code to run it was full of bugs and issues. I suspect that the code you posted isn't your production code, but an example you wrote specifically for this question. I suggest that you try to make a running compilable example before posting.
Nevertheless, my suggestion should work for you with a little tweaking.
It is multi-threaded and thread-safe. And it does do cleanly save the modified collection when done.

how to get return value when a thread is done

I am trying to do a long process with multi threads. How can I get resutn value from threads? If I can get return values, I will update database according to the return value.
here is my code that calls the threads...
foreach (var obj in elements) {
string body_ = #"<html><head></head><body><a href='http://localhost:5111/Default.aspx?id=" + obj.ID + #"&answer=yes'>Evet</a> - <a href='http://localhost:5111/Default.aspx?id=" + obj.ID + #"&answer=no'>Hayır</a></body></html>";
Thread thread = new Thread(() => sendEmailThread(obj.ALICI, obj.KONU, body_));
thread.Start();
}
here is the thread invoker.....
private void sendEmailThread(string ALICI, string KONU, string body_)
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate() { sendEmail(ALICI, KONU, body_); } );
}
here is the real email sender
public int sendEmail(string to_,string subject_,string body_) {
.......
.......
.......
return 1;
}
Instead of using thread you could using Task which offers many advantages over creating yourself the threads
Your code can be changed to something like this:
var task = Task<int>.Factory.StartNew(
() => sendEmailThread(obj.ALICI, obj.KONU, body_));
task.ContinueWith(() => {var result = task.Result;});
Update
To get a behavior similar to BackgroundWorker you should synchronize your task with the current UI thread using the TaskScheduler.FromCurrentSynchronizationContext()
so the code above can be written like this :
var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();
task.ContinueWith(() => { var result = task.Result; }, UISyncContext);
This article illustrate why you should use Task.Run over BackgroundWorker
In this situation you could just put your code at the end of sendEmail, or as part of the delegation to create the thread like:
private void sendEmailThread(string ALICI, string KONU, string body_)
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate() {
var result = sendEmail(ALICI, KONU, body_);
SomeDBUpdate(result);
} );
}
Even better might be put an eventhandler into your code called something like "OnEmailSendComplete" and pass the return type into the handler.
That said, it's neater to use the new Async methods if the above is all your code is doing rather than writing your own thread handling.
In .NET exists BackgroundWorker class which is the right way to handle time-consuming tasks in background. Try something like this:
foreach (var obj in elements) {
BackgroundWorker bw = new BackgroundWorker();
string body_ = #"<html><head></head><body><a href='http://localhost:5111/Default.aspx?id=" + obj.ID + #"&answer=yes'>Evet</a> - <a href='http://localhost:5111/Default.aspx?id=" + obj.ID + #"&answer=no'>Hayır</a></body></html>";
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync(-- your params here --)
}
You need to create DoWork and RunWorkerCompleted handlers, but I'm sure you can find a lot of tutorials over the internet, for example:
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
Sending Arguments To Background Worker?

Threads problems with return value

I'm trying to start one thread, receive string from it and then start n-counts of threads with the received string. Code:
private void button2_Click(object sender, EventArgs e)
{
string post = null;
sync = new ManualResetEvent(false);
var thr = new Thread[1];
thr[0] = new Thread(delegate() { post = create_note(); });
thr[0].IsBackground = true;
thr[0].Start();
sync.WaitOne();
decimal value = Program.Data.numericUpDown1;
int i = 0;
int j = (int)(value);
thr = new Thread[j];
for (; i < j; i++)
{
thr[i] = new Thread(() => invite(post));
thr[i].IsBackground = true;
thr[i].Start();
}
}
public string create_note()
{
while (true)
{
string acc = "";
string proxy = "";
if (Program.Data.checkBox1 || Program.Data.checkBox2)
{
if (Program.Data.checkBox1)
Proxy.type = "http";
else if (Program.Data.checkBox2)
Proxy.type = "socks5";
lock (locker)
{
if (Proxy.proxies.Count == 0)
{
foreach (string prox in File.ReadAllLines(proxy_path))
{
if (prox.Contains(":"))
Proxy.proxies.Add(prox);
}
}
}
proxy = rand_proxy();
}
else if (!Program.Data.checkBox1 && !Program.Data.checkBox2)
Proxy.type = "none";
if (edit_accs.Count == 0)
{
break;
}
else
acc = edit_accs.Dequeue();
Od_post od_post = new Od_post(acc, proxy, Proxy.type);
string login = od_post.Auth();
if ()
{
string url = rand_url();
var text = new RandomString(Program.Data.textBox3).ToString();
string wall_post_text = od_post.wall_post_text(get_text(text), url);
if (wall_post_text == "Good")
{
string image_add = od_post.image_add(post_image_path);
if (image_add.Split('|')[0] == "Good")
{
if (Program.Data.checkBox5)
{
string change_name = od_post.change_name();
if (change_name == "Changed")
{
}
else
{
}
}
sync.Set();
return image_add.Split('|')[1];
}
else
{
}
}
else
{
}
}
else
{
lock (locker)
{
accs.Enqueue(acc);
Proxy.proxies.Remove(proxy);
}
}
}
return "Failed";
}
But it doesn't work. My app hangs, and the post doesn't receive the return value from create_note(). Why?
thr[0].Start();
sync.WaitOne();
This is a pretty common mistake when using a thread. Threading can give you two benefits. One is that code can run concurrently, allowing you to get more work done if you have a machine with multiple cores. And more commonly, it can run code asynchronously, a consideration when you have a user interface that you want to keep responsive.
You are getting neither. It does not run concurrently, your main thread isn't doing any work since it is waiting for the thread to complete. And it doesn't run asynchronously either, your main thread is frozen while the thread is doing its job.
You instead get all the disadvantages of a thread. A classic threading bug is a "race", your code has one. You hope that the "post" variable is assigned after the WaitOne() method completes. It might be, but the odds are just not that good. Since you call Set() before the assignment executes. It can only work correctly when you call Set() after the assignment. Deadlock is another classic threading bug, I don't see one but your debugger can easily show you. Deadlock is very likely here because you are freezing your main thread. Making calls like Invoke() on the thread is going to deadlock.
There's a very simple substitute for your code that does everything that you hope your current code does, minus the threading bugs:
string post = create_note();
Problem solved.
Minus addressing the reason that you considered writing this code in the first place. Which does require that you move all of the code that's now after the WaitOne() call into a callback that runs when the thread completes. Such code tends to be difficult to write, dealing with asynchronicity isn't that easy. But you can get help from .NET to get this right, like the BackgroundWorker.RunWorkerCompleted event, the Task class with its TaskScheduler.FromCurrentSynchronizationContext() method. And the async keyword added to C# version 5.

Categories

Resources