0

I have a scenario where I have two threads - main and worker (producer/consumer) The main thread creates the worker and waits for its termination. After completion the main thread wants to access the result of the worker.

private object result;
private Exception exception;

public object Connect(int timeout)
{
    Thread thread = ThreadHelper.StartNewBackground("Worker Thread", ConnectImpl);

    if(!thread.Join(timeout))
        throw new TimeoutException(string.Format("Connecting to the remote device has timed out ");

    if (exception != null)
        throw new ConnectFailedException(exception);

    return result;
}

private void ConnectImpl()
{
    try
    {
        result = ConnectToARemoteDevice();
    }
    catch(Exception ex)
    {
        exception = exc;
    }
}

I am not sure about the synchronization of the results (result and exception field). In my opinion there is a possibility that the main thread does not see the updaed values. Therefore I would reimplement ConnectImpl() into:

private void ConnectImpl()
{
    try
    {
        Interlocked.Exchange(ref result, ConnectToARemoteDevice());
    }
    catch(Exception ex)
    {
        Interlocked.Exchange(refexception, ex);
    }
}

Or maybe I am wrong and it is not necessary? I am not sure if this necessary because for the following snippet run with optimization it does not run undefinetly:

bool complete = false; 
var t = new Thread (() =>
{
complete = true;
});
t.Start();
t.Join(); // comment this line to run undefinetly
bool toggle = false;
while (!complete) toggle = !toggle;
complete = true;

Is Join making some MemoryBarrier? I am using .NET 3.5 so I cannot use TPL, but maybe you can suggest another mechanism/implementation?

Solved Are memory-barriers required when joining on a thread? Join is making a MemoryBarrier.

Community
  • 1
  • 1
Pellared
  • 1,242
  • 2
  • 14
  • 29
  • 1
    I suggest using a [`BackgroundWorker`](http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker%28v=vs.110%29.aspx). See the link for full details! – Matthew Watson Mar 13 '14 at 12:15

3 Answers3

0

Join method makes that calling thread blocks until running thread finishes, so it is enough well synchronization. But it's not multithreading if you create another thread and wait to it to finish, why not run that code in current thread? There are many different scenarios in multithreaded environment, but code snippet you provided is OK, without last loop you should have complete == true, but there is something more... Always check if thread is alive before calling Join:

if (t.IsAlive) t.Join();

Join have noting to do with the memory, memory is always shared. I'm running your code in debug without blocking, with or without Join.

Pellared
  • 1,242
  • 2
  • 14
  • 29
Elvedin Hamzagic
  • 825
  • 1
  • 9
  • 22
  • No the last loop is OK. I want to wait until the worker thread finishes the job. IsAlive is not necessary beacuse I have invoked t.Start(); – Pellared Mar 13 '14 at 12:49
  • You never know when thread will finish. Your thread is only one statement, so its more likely that it will finish maybe in next line in calling thread. Or if you insert breakpoint in Join()... – Elvedin Hamzagic Mar 13 '14 at 13:05
  • You are also not right for the memory sharing. The processor are having caches that needs to be synchronized. Run the snippet in Release mode with the Join commented and you will probably see that it will never end. – Pellared Mar 13 '14 at 16:37
0

Join is making a MemoryBarrier. So no other synchronizing mechanism is needed. Resource: Are memory-barriers required when joining on a thread?

Community
  • 1
  • 1
Pellared
  • 1,242
  • 2
  • 14
  • 29
0

This is really interesting little problem, so I must reply in some details. Yes, you are right, in release mode without Join it will run forever. But that code snippet is a bit strange, you probably would never write something like that.

If you add thread sleep like this:

while (!complete) { toggle = !toggle; Thread.Sleep(1); }

your program will exit regularly, so that works also.

Nevertheless, if you replace Thread.Sleep with MessageBox.Show, it will also exit regularly, or if you just add some extra code, it works also.

bool complete = false; 
var t = new Thread (() =>
{
    /*MessageBox.Show("Thread started.");*/ // comment this out to not confuse you.
    complete = true;
    /*MessageBox.Show("Thread finished.");*/
});
t.Start();
//t.Join(); // comment this line to run undefinetly
bool toggle = false;
DateTime time = DateTime.Now;
while (!complete) {
    toggle = !toggle; /*Thread.Sleep(1);*/
    time = DateTime.UtcNow; }
MessageBox.Show("Main thread finished: " + time.ToString());
complete = true;

I don't know what is exactly behind this, it's sometimes hart to know, but make some tests, and the most important, rely on your own intuition.

Elvedin Hamzagic
  • 825
  • 1
  • 9
  • 22
  • Nice research! Everything that needs process context switching makes a memory barrier! `Thread.Sleep()` does it beacause it suspends the thread, `MessageBox.Show()` also because it is invoked on another (UI) thread and then it returns back (context switch) to the worker thread. I do not know if I explained it well... Please comment if it is clear to you. – Pellared Mar 20 '14 at 10:25