3
\$\begingroup\$

Using C#, create a class which gets an action (parameter-less delegate) in its construction and has a single public method bool execute() which does the following:

  • Executes the action only if no other thread is currently executing the action

  • If another thread is already executing, wait until thread is finished before returning (but without executing action again)

  • Return true if current thread executes that action, otherwise return false.

public delegate bool Action();

private static int ThreadsCount = 100; //initialize threads count

static void Main(string[] args)
{
    Action act = Execute;
    bool t = act();
    Console.WriteLine(t);
    Console.ReadLine();
}

static bool Execute()
{
    bool retVal = false;
    Thread thread = Thread.CurrentThread;
    string s = thread.Name;
    if (s == null)
    {
        test();
        retVal = true;
    }
    else
    {
        ThreadsCount = ThreadsCount - 1;

        if (ThreadsCount < 1)
        {
            //if all threads finished executing do whatever you wanna do here..
            Console.WriteLine("C# language");
            test();
            retVal = true;
        }
        else
        {
            retVal = false;
        }

    }
    return retVal;
}

private static void test()
{
    Console.WriteLine("C# language");
}

Is the above piece of code correct? Please give me your feedback.

Please don't think that this is just exercise. This question was asked by one of the interviewers.

\$\endgroup\$
  • \$\begingroup\$ wait until thread finished before returning - I do not see any code which will do it. create class which get an action (parameter less delegate) in its construction - you didn't show any class or constructor in your code. It's hard to review your code, it has some weird logic (Thread.CurrentThread.Name == null) and I do not see what this code should do. \$\endgroup\$ – Snowbear Jun 11 '11 at 8:25
4
\$\begingroup\$

You need to look at how you can make the Execute method both thread-safe and true to the requirements of the method. You say that it only should return true in the case where the delegate is actually called, but you need to make sure that only one thread can call it at a time. That's where lock comes in. You could potentially do something like:

public class SingleCallAction
{
  private readonly object sync = new object();
  private readonly Action action;
  private bool executed;

  public SingleCallAction(Action action)
  {
    if (action == null)
      throw new ArgumentNullException("action");

    this.action = action;
  }

  public bool Execute()
  {
    if (!executed)
    {
      lock(sync)
      {
        if (!executed)
        {
          action();
          executed = true;
          return true;
        }
      }
    }

    return false;
  }
}

Now, what this class provides is a wrapper around an Action delegate. We use Action because it supports both delegate assignment, or lambda assignment.

The Execute method will first check to see if it has previously been executed (if it has, it will jump down to the return false; as its not the thread that has executed the delegate.

If if hasn't executed, it will lock the sync object to prevent any other threads from executing the inner code block. The compiler is generating Monitor.Enter and Monitor.Exit calls here for you, worth a read up!

After the lock, we check whether the executed flag is set again. This allows us to prevent race conditions for competing threads. Don't worry about race conditions when dealing with the executed flag, as variable reads/writes are always atomic operations (mostly).

If we are the first thread to be executing the delegate, we can then execute it action(), set the flag as executed, and return true.

Any subsequent calls will always result in false.

To use in code:

var action = new SingleCallAction(() => Console.WriteLine("Executed"));

var thread1 = new Thread(() => Console.WriteLine(action.Execute()));
var thread2 = new Thread(() => Console.WriteLine(action.Execute()));

thread1.Start();
thread2.Start();

Should result in:

Executed
True
False
\$\endgroup\$
  • \$\begingroup\$ Assuming that we use the memory model of the ECMA specification, your code is not guaranteed to wait until the action is executed. You must have a memory barrier before setting executed true. Otherwise the code can be reordered (by compiler, jit-compiler or processor) and the executed might be set before action() is called. Anyway, +1 for a good answer. \$\endgroup\$ – mgronber Jun 13 '11 at 19:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.