LO's Sharp Devs

C# , XNA Game Development Blog. Intentionally more than only for game authors but for C# programmers in general.

Moje zdjęcie
Nazwa:
Lokalizacja: Poland

środa, 8 października 2008

[Coding] Dealing with multithread

Today's thing that may be little tricky for a newbie developer, causing sometimes a bunch of frustration - I mean dealing with access to main-thread-only objects [MTOOs] (like windows and (suggested) files) form other threads.

Using BackgroundWorker.OnProgress seems to work fine, and does not require writing synchronization code. But what with other threads or accessing MTOOs directly from OnWork method?
Solution is fairy well designed by .Net designers and simple but little troblesome to implement. When in need to display some text for example in "textarea" one could just write:
form1.textLog.AppendText("some text");

But this would just throw InvalidOperationException. What do then?
One must use delegates. So first of all - code that will be synchronized shall be placed into separate method. There are two possible ways to do, because of delegate required:
1. Use one of the existing delegates (EventHandlers) and create method's arguments to fit it.
2. Write your own simple delegate to pass required set of arguments with it.
Sample declaration may look like:
public delegate void DebugMessageInvocation(string message, ErrorSeverity severity);

And then just delegated method:
private void WriteLog(string message, ErrorSeverity severity)
{
  form1.textLog.AppendText(message);
}

And we just call the function instead:
WriteLog("some text", ErrorSeverity.Info);

Of course this will not fix the problem yet, nor it explain why did we used delegate parametrization. The answer is System.Control.Invoke() method. It's supposed to execute given (by delegate) method in context of the main thread! Yes - it is what we were looking for. And why did we required delegate. Now it can be implemented in two ways: making delegate call directly from "main" code, or encapsulating all the logic inside delegate function using some simple "tricks":
private void WriteLog(string message, ErrorSeverity severity)
{
  if (form1.textLog.InvokeRequired) // yes, we are checking if we are already in the main thread, very nice thing :)
  {
    form1.textLog.AppendText(message); // safe, we are already in main thread
  }
  else
  {
    form1.textLog.Invoke(new DebugMessageInvocation(WriteLog),
      new object[] { message, severity }); // [1]
  }
}

Eventually one may negate condition and put all the logic further in the method body - preferred way of doing things.
What is going on up there? I think that only required explanation is for line [1]. Is what happen in the way:
1. We are building array of all arguments passed to the method - we are responsible for right order etc.
2. We create back delegate for the very same method we are in
3. We order System.Control to invoke this delegate in the main thread context and pass to this given array of parameters (will be unwrapped on the way).
And that's all - when function is invoked, it just passes to the main branch and does what it was supposed to do.

One important thing to add: I was talking about Invoke being executed into the main thread, what is not essentially the truth. In fact both using controls require and invoking code results with is to be in the same thread that when control was created. While majority of controls is being created in the main thread this works fine. So I strictly recommend to create controls from inside the main thread - and the best solution for that is to use... Invocation again (using a main form control for that)!

Have nice time. Hope this will be something new and interesting for some devs :D

Komentarze (0):

Prześlij komentarz

Subskrybuj Komentarze do posta [Atom]

<< Strona główna