ThreadPool.QueueUserWorkItem with a lambda expression and anonymous method

25,924

Solution 1

There is nothing wrong with this. The compiler is essentially doing automatically what you described as your alternative. It creates a class to hold the captured variables (test, s1 and s2) and passes a delegate instance to the lambda which is turned into a method on the anonymous class. In other words, if you went ahead with your alternative you would end up with soemthing very similar to what the compiler just generated for you.

Solution 2

For this particular example, no there is nothing wrong here. The state you've passed into the other thread is wholely contained and none of the types involve have any thread affinity issues.

Solution 3

One potential problem with the pattern is that it's very tempting to expand it into something more-generic but less-safe like this (scratch code- don't expect it to work):

public static void QueueTwoParameterWorkItem<T1, T2>(T1 value1, T2 value2, workDelegate<T1,T2> work)
{
    try
    {
        T1 param1 = value1;
        T2 param2 = value2;
        ThreadPool.QueueUserWorkItem(
            (o) =>
            {
                work(param1, param2);
            });
    }
    catch (Exception ex)
    {
        //exception logic
    }
}

Solution 4

It's a nice way of doing it. I don't see any disadvantages of using lambdas. It's simple and clean.

Solution 5

What you are looking at is refered to as a closure. As chuckj states, the compiler is generating a class at compile time which corresponds to the members that are accessed outside of the closure.

The only thing you have to worry about is if you have ref or out parameters. While strings are immutable, the references to them (or any variable) are NOT.

Share:
25,924

Related videos on Youtube

Scott Whitlock
Author by

Scott Whitlock

By day I'm a Professional Engineer, currently working as a .NET software developer. I also wrote and maintain an open source extensible application framework called SoapBox Core, and an open source C# library for communicating with Insteon home automation devices called FluentDwelling. I have decided to stop contributing to this community because it has become a site more concerned with nitpicking and rules than with allowing programmers to help other programmers.

Updated on July 09, 2022

Comments

  • Scott Whitlock
    Scott Whitlock almost 2 years

    Passing two parameters to a new thread on the threadpool can sometimes be complicated, but it appears that with lambda expressions and anonymous methods, I can do this:

    public class TestClass
    {
        public void DoWork(string s1, string s2)
        {
            Console.WriteLine(s1);
            Console.WriteLine(s2);
        }
    }
    
    try
    {
        TestClass test = new TestClass();
        string s1 = "Hello";
        string s2 = "World";
        ThreadPool.QueueUserWorkItem(
            o => test.DoWork(s1, s2)
            );
    }
    catch (Exception ex)
    {
        //exception logic
    }
    

    Now, I've certainly simplified this example, but these points are key:

    • The string objects being passed are immutable and therefore threadsafe
    • The s1 and s2 variables are declared within the scope of the try block, which I exit immediately after queuing the work to the thread pool, so the s1 and s2 variables are never modified after that.

    Is there something wrong with this?

    The alternative is to create a new class that implements an immutable type with 3 members: test, s1, and s2. That just seems like extra work with no benefit at this point.

    • mmx
      mmx about 15 years
      Why don't you just write o => test.DoWork(s1, s2) instead of the more verbose definition?
    • Scott Whitlock
      Scott Whitlock about 15 years
      @Mehrdad: Because I'm really new to lambda expressions. ;) - thanks!
    • Scott Whitlock
      Scott Whitlock about 15 years
      @Mehrdad: I changed it in the question.
  • Joel Coehoorn
    Joel Coehoorn about 15 years
    What about the general pattern, then? How would know if a type has thread affinity issues?
  • Scott Whitlock
    Scott Whitlock about 15 years
    Basically you're asking if a class is threadsafe or not. In my particular implementation (in most cases) I'm using deeply immutable objects to make them threadsafe. Other ways to make objects threadsafe is to use locking, etc.
  • JaredPar
    JaredPar about 15 years
    @Joel if a type has thread affinity issues, you're toast. You can do nothing with it on a separate thread. The general pattern is sound though (I use it often)
  • JaredPar
    JaredPar about 15 years
    @Scott, no. ThreadAffinity is very different than thread safe. Thread affinity means an object can only be used from a particular thread (think WinForms, WPF).
  • Scott Whitlock
    Scott Whitlock about 15 years
    In my case, I have a deeply immutable base class that I could use to nail down T1 and T2... so you could make this work in my specific case. If you don't understand the threading issues, then using the threadpool is out of the question anyway.
  • Scott Whitlock
    Scott Whitlock about 15 years
    @JaredPar, sorry, you are correct. You're referring to whether or not the DoWork function tries to access a control on a form, for instance. In that case you'd have to check if an Invoke is required, and then do the Invoke if necessary.
  • Joel Coehoorn
    Joel Coehoorn about 15 years
    Agreed: your specific case is fine. It's when you implement it as a generic pattern there are concerns.