Adventures in C# and Windowsland, part 2b – Threading/Event loops

I thought that my previous post needed a follow-up, after the whole discussion and what happened.

Before stepping on, I would like to thank everyone who commented and gave me some precious advices. I really appreciate it. Now, to the point.

I discovered (thanks to previous comments) the existance of BackgroundWorker, that I still didn’t understand if it’s part of Windows.Forms or not (belongs to System.ComponentModel namespace but somehow makes me feel it’s part of the Forms stuff). So I ditched my ExtendableThread and switched to it.

I liked the fact that you can report progress just by calling an event that reports a percentage. Pretty nice, especially for some basic usages, where it really saves you the hassle of reimplementing such small things on your own. Thumbs up to that. Solved the threading stuff, it was now time to make the threads talk one another. And here came the point where I needed to start messing with my own events.

Events have some points stronger than Qt’s Sig/Slots, and some points weaker. Let’s get through them.

Declaration: As any Qt developer might notice, declaring an event or a signal/slot connection takes exactly the same amount of code. To roll down my own event, it came up to this:


public delegate void ExchangeCompletedHandler(object source, Message reply);
[...]
public event ExchangeCompletedHandler ExchangeCompleted;
[...]
ExchangeCompleted(this, reply);

vs


Q_SIGNALS:
void ExchangeCompletedHandler(Message reply);
[...]
emit ExchangeCompleted(reply);

Ok, you actually see it takes one line less in Qt. Anyway, declaring a delegate is both a nice and a odd feature. The plus is that safety of connections is determined at compile time, and there’s no way in the world that a connection might fail at runtime (with Qt it can). On the other side, I like to do this sometimes:


connect(blah, SIGNAL(test(int,QString,QWhatever)), this, SLOT(plainTest()));

Not having a compile-time check lets you create dynamic connections that doesn’t force you to have the same parameters as the signal, but one of them, none, etc. In C# that would require declaring a delegate for each form of your “slot”. But in the end, I think the approach that C# takes can give some more assurance to the inexperienced programmer, so I guess they’re even.

Calling the “slot”: One other thing you might have noticed by comparing the 2 declarations is that the Qt one lacks a parameter. Or better not, you can retrieve the sender just by calling sender() inside the slot. Qt’s metaobject rocks definitely, and that’s something I miss in C#. This also made me wonder if I could fake the streamer when streaming an event in C#, since I have to declare this explicitely. I didn’t try, but I hope there is actually some way of checking this, otherwise it might lead to some unexpected results to the poor programmer.

So far so good, I think I’m quite satisfied with C# events. But then again failure struck me. I came to write that bunch of code:

IAsyncResult res = client.BeginConnect(serverBox.Text, Convert.ToInt16(portBox.Text),
new AsyncCallback(register_ConnectCallback), null);
}

private void register_ConnectCallback(IAsyncResult rs)
{
client.EndConnect(rs);
// Let's generate the XML request
RegistrationRequest req = RegistrationRequest.createRegistrationRequest(usernameBox.Text, passwordBox.Password);

So far so good, but my program wouldn’t work without telling me anything. After some weird try to see what was wrong, I tried debugging it, and I got stuck with an Exception that was not notified during the application run (very, very bad): “Can’t access Text since the object resides in another Thread”.

1 point more for throwing exceptions at this. It is actually nice that being non thread safe is considered an error at runtime, from my point of view. 1 less for not notifying me and not quitting the application and simply destroy the thread. 1 less since why on earth should I be in a different thread. Ok, nevermind that for now, and let’s try this hack:

Dictionary data = new Dictionary();
data.Add("username", usernameBox.Text);
data.Add("password", passwordBox.Password);

IAsyncResult res = client.BeginConnect(serverBox.Text, Convert.ToInt16(portBox.Text),
new AsyncCallback(register_ConnectCallback), data);
}

private void register_ConnectCallback(IAsyncResult rs)
{
client.EndConnect(rs);
// Let's generate the XML request
Dictionary data = (Dictionary)rs.AsyncState;
RegistrationRequest req = RegistrationRequest.createRegistrationRequest(data["username"], data["password"]);

Works, but it has a really bad shape. BeginConnect explicitely gives you the opportunity to pass some data in form of a generic object to retrieve it in the result. So this made me wonder if it was actually AsyncCall fault and it wouldn’t happen with events. I refactored the code to make the same function being an handler of an event that was streamed in the very same thread (I called Connect instead of BeginConnect and then streamed the event). Guess what? Same result.

This boils down to my very first concern about events: they’re belonging in a different thread, so thread safety goes to hell (it was definitely not the Timer, as you can see). The problem is, that hack does not suffice. I need to do this:


Storyboard cstory = (Storyboard)Resources["FeedbackAnimation"];
cstory.Begin();

To notify the user of the progress, and I obviously can’t. I’m stuck finding a way that will make me be able to do this without passing the Resources object to the thread (that, honestly, I don’t even think will work), also because I can’t overload my delegates with some parameters to pass the event custom data. I think there’s got to be a way, otherwise C# has somehow a big problem. The very first scope of using async methods in GUI applications is to have the gui responsive and give feedback to your user. What if the design of events prevents you from doing that?

For reference, Qt’s signals and slots are thread safe, and slots are delivered to each thread’s event loop, so that you are sure that your connection happens safely. You can even post events to the event loop of each thread in a completely thread safe way, if that’s not enough. I am fairly much missing that in C#, and I hope I can find a solution for this.

Spoiler on next post: Handling of data (XML and DataSource)

UPDATE: Found out about this.Dispatcher.Invoke(new Action(delegate() { cstory.Begin(); })); . Not very elegant and depends on WPF, but works. I’ll search for a better solution

~ by drfav on 15 April, 2009.

3 Responses to “Adventures in C# and Windowsland, part 2b – Threading/Event loops”

  1. ExchangeCompleted(this, reply);

    Be careful here – the event can be null if no-one has connected to it.

    The recommended way of calling it is:

    // this line is to prevent race conditions where the
    // last listener might be removed between the null
    // check and the call
    ExchangeCompletedHandler evt = ExchangeCompleted;
    if (evt != null)
    evt(this, reply);

    I can see why they did it this way (the null check allows for optimisations where you don’t calculate some value if no-one is going to use it), but it’s really annoying in the common case.

  2. Thanks for your hint, it saved my day!

  3. I don’t have ATT&T and i wanted an iphone, but getting one would be expensive. But i really love the apps and software. Then i realized the ipod touch is almost exactly the same! What are the specific differences between them? is the price the same?

    ________________
    unlock iphone 3gs

Leave a Reply