Chris Skardon 的个人资料Chris Skardon日志列表 工具 帮助

日志


2007年6月

Generic Parameterized Threads

I do quite a bit of multi-threading at the moment, which a long with all the inherent problems (concurrency, complexity etc) makes for some very interesting work. With .NET 2.0 we were gifted with the ParameterizedThreadStart delegate for us to use, which saved a lot of work-arounds involving member variables (and in one case - an intricate queuing system). One thing still puzzles me though - why didn't Microsoft didn't make the ParameterizedThreadStart generic, instead of taking just an object as an argument.

The ParameterizedThreadStart delegate is defined as so:

public delegate void ParameterizedThreadStart(object obj);

This is used when you want to create a new thread and pass an argument to it, (in itself, a big step up from the .NET 1.1 days, when all we had was 'ThreadStart'). The problem is, this now adds an element of un-compile-time-safety to the code, I can pass anything, and will only know about it at run-time. I guess the ideal solution to this from my point of view would be something like:

public delegate void ParameterizedThreadStart<T>(T arg);

Which would allow me to then call the method I want to start on a new thread type-safely.

So, lets start back at the beginning, how do we use the current ParameterizedThreadStart? I have the following method that I want to run in a new thread:

private void Inty(int anInt)
{
int sleepTime = 1000 / anInt;

for (int i = 0; i < anInt; i++)
{
Console.WriteLine("An Int! " + anInt);
Thread.Sleep(sleepTime);
}
}

It's a very simple method, that writes out a message from a for loop, and sleeps for a set time. For me to use this with the current ParameterizedThreadStart I would do this:

Thread t = new Thread(new ParameterizedThreadStart(Inty));
t.Start(5);

Which also requires a change to the 'Inty' method:

     private void Inty(object obj)
{
int anInt = Convert.ToInt32(obj);
...
}

Which means I lose all my typesafety, as I can now call my thread such:

t.Start("HULLO!");
t.Start(new object());
t.Start(null);

All valid compile-wise, not good at run-time, as a consequence, I should add some validity checks into 'Inty'. But I just would rather not have to do this.

This brought me to write a wrapper for the Thread class, I called it 'Thread' (but in a different namespace), so the code would look the same;

First I needed a generic ParameterizedThreadStart delegate:

public delegate void ParameterizedThreadStart<T>(T argument);

Now I've got that, it's time to create a wrapper, we can't extend System.Threading.Thread as it's a sealed class, so wrapping is our lot. The idea is that I want to be able to create my new thread, and then call 'Start' on it just as a normal thread.

My solution is below;

using System.Threading;
public class Thread<T>
{
public string Name { get { return mTheThread.Name; } set { mTheThread.Name = value; } }

private Thread mTheThread;
private ParameterizedThreadStart<T> mTheDelegate;

public Thread(ParameterizedThreadStart<T> theDelegate)
{
mTheDelegate = theDelegate;
mTheThread = new Thread(new ParameterizedThreadStart(Run));
}

public void Start(T theArgument)
{
mTheThread.Start(theArgument);
}

private void Run(object arg)
{
T theArgument = (T) arg;
mTheDelegate(theArgument);
}
}

I wrap a System.Threading.Thread which I construct and initialise in the constructor. The ParameterizedThreadStart<> which I pass in to the constructor I store as a member variable, ready to be run in the private 'Run' method. The Run method is a match to a standard ParameterizedThreadStart delegate, and performs a cast to the correct type. This cast is safe, as the whole class is made 'generic'.

So, to use the new Thread class:

Thread<int> t = new Thread<int>(new ParameterizedThreadStart<int>(Inty));
t.Start(5);

I don't have to make any changes to 'Inty' which is nice, and the compiler will complain if I try to call 'start' with any value other than an int. Equally, if I try to set up the ParameterizedThreadStart<> with anything other than an int the compiler will complain.

The only thing I don't really like is the way I have to put 'int' 3 times in that constructing row, I would prefer something like:

Thread t = new Thread(new ParameterizedThreadStart<int>(Inty));

but, for type safety I can live with it.

2007年6月

That towel trick...

Hrumph! Not effective in my case... The ol' XBox is packed up and ready for shipping to MS' base in the UK.

More weeks wait for Forza!

2007年6月

XBox 360 Red Lights of Death...

So, here we are, my second xbox 360 has died now, though this time (due to the support line being closed) I perused the masses of online stuff about it and came across a 'Towel' trick - well, to be honest, my girlfriend remembered reading an article in the Guardian. As a result I googled for 'PacoDG towel' and found many-a-page, but in particular an entry by PacoDG on XBox 360 Rally.

Currently the xbox is under the towels, so to speak - 30 seconds to go...

2007年6月

First Laptop post!

Huzzah! For the first time ever I finally own a laptop! A Sony VAIO VGN-C290, a nice dual core pc ready for my development trials!

Hopefully I'll be able to get a lot more stuff done now and write to this blog more, though we'll have to see how that pans out :)

2007年6月

Synchronizing with external changes...

Has anyone else seen this message? It appears when go back to my Visual Studio instance containing my (admittedly too large) solution of 170 odd projects (or maybe it's not too large??).

SynchronizingExternalChanges

The mouse cursor goes into 'busy' mode and I basically can't do anything with visual studio (it goes into 'Not Responding' mode - hence the pale look of the screen shot).

I use Sourcegear Vault as my source provider, and also run 'Resharper 2.5'  and the SQL 2005 addins in this instance.

It's really frustrating, and I haven't seen anything online about this, i.e. I have yet to find anyone else who has discussed it. A couple of my colleagues here do suffer from the same problem, so at least I know it's not isolated to me.

Anyone else suffer from this pain?

TypeLoadExceptions

Occasionally we get TypeLoadExceptions from .NET code, you can try all manner of programs to try to figure out what the problem is - 'Dependency Walker' is always a good start - but I would recommend before even going down that route -

Ensure you are running the right version of the dlls

Always, and I mean always clean the solution (right-click -> clean) and then rebuild if you get a TypeLoadException. This will rule out a large number of problems. Clearly this isn't the only way to get this exception, but it's a good thing to check first.