views:

137

answers:

6

Hi, I'm new to threading in C#. Is there anyway of setting a timeout for a thread without blocking the calling thread (in C# 3.5)?

If not, is it logical to execute a function using a thread and within that function create a thread and join it to overcome this main thread blocking issue? To illustrate:

Instead of:

Public void main()
{
        ...
        Thread thrd1 = new Thread(new ThreadStart(targetObj.targetFunc));
        thrd1.Start();
        thrd1.Join();
        ...
}

Using something like:

Public void main()
{
        ...
        Thread thrd1 = new Thread(new ThreadStart(middleObj.waiter));
        thrd1.Start();
        ...
}

//And in the middleObj.waiter():
Public void waiter()
{
        Thread thrd2 = new Thread(new ThreadStart(targetObj.targetFunc));
        thrd2.Start();
        thrd2.Join();
}

Thanks...

A: 

The easiest thing to do is to call Thread.Join at safe points from your main thread and pass in the amount of time you want to wait for the join to occur.

public static void Main()
{
  TimeSpan timeout = TimeSpan.FromSeconds(30);
  Thread thread = new Thread(() => { ThreadMethod(); });
  thread.Start();
  DateTime timeStarted = DateTime.UtcNow;
  DoSomeWorkOnThisThread();
  // We are at a safe point now so check the thread status.
  TimeSpan span = DateTime.UtcNow - timeStarted; // How long has the thread been running.
  TimeSpan wait = timeout - span; // How much more time should we wait.
  if (!thread.Join(wait))
  {
    thread.Abort(); // This is an unsafe operation so use as a last resort.
  }
}
Brian Gideon
+1  A: 

You might also want to take a look at ThreadPool.QueueUserWorkItem() ( http://msdn.microsoft.com/en-us/library/kbf0f1ct.aspx ) which does a lot of stuff for you.

As Brian commented, aborting a thread is usually not a smart thing to do, because it could be in the middle of doing some important stuff at that moment.

deltreme
A: 

"Join member--> Blocks the calling thread until a thread terminates, while continuing to perform standard COM and SendMessage pumping." MSDN website.

thrd1.Join() tell the calling thread to wait until the completion of the thrd1.

My favorite solution is to make a small class which i'm able to control the execution of thread.

public class MyClass
    {
        private bool _stop;
        private Thread _myThread;

        public void Stop()
        {
            _stop = true;
            //Will block the calling thread until the thread die
            _myThread.Join();
        }

        public void Run()
        {
            _stop = false;
            _myThread = new Thread(Work);
        }

        public void Work()
        {
            do
            {

            } while (!_stop);
        }
   }
Jean-Christophe Fortin
A: 

Look at WaitHandle.WaitOne() method with the middleObject scheme.

Public void main()
{
    ...
    middleObj.WaitHandle.Reset();
    Thread thrd1 = new Thread(new ThreadStart(middleObj.waiter));
    thrd1.Start();
    middleObj.WaitHandle.WaitOne(timeout);
    ...
}


//And in the middleObj.waiter():
Public void waiter()
{
    Thread thrd2 = new Thread(new ThreadStart(targetObj.targetFunc));
    thrd2.Start();
    thrd2.Join();
    this.WaitHandle.Set();
}

Not sure what would happen to the unfinished thread, though.

apoorv020
A: 

You can start a System.Threading.Timer for each thread and pass it the thread's ManagedThreadId. Keep dictionaries for the active threads and their timers, keyed by the ManagedThreadId. If a timer expires, use the passed thread ID to abort the thread and kill its timer. If the thread finishes normally, invoke a callback that kills the timer. Here's a simple console example:

using System;
using System.Collections.Generic;
using System.Threading;

namespace ConsoleApplication2
{
    public delegate void KillTimerDelegate(int arg);

    class Program
    {
        static Dictionary<int, Thread> activeThreads = new Dictionary<int, Thread>();
        static Dictionary<int, Timer> activeTimers = new Dictionary<int, Timer>();
        static void Main(string[] args)
        {
            for (int i = 0; i < 10; i++)
            {
                Worker worker = new Worker();
                worker.DoneCallback = new KillTimerDelegate(KillTimer);
                Thread thread = new Thread(worker.DoWork);
                activeThreads.Add(thread.ManagedThreadId, thread);
                thread.IsBackground = true;

                thread.Start();
                Timer timer = new Timer(TimerCallback, thread.ManagedThreadId, 500, 500);
                activeTimers.Add(thread.ManagedThreadId, timer);
            }
            Console.ReadKey();
        }

        static void TimerCallback(object threadIdArg)
        {
            int threadId = (int)threadIdArg;
            if (activeThreads.ContainsKey(threadId))
            {
                Console.WriteLine("Thread id " + threadId.ToString() + " aborted");
                activeThreads[threadId].Abort();
                KillTimer(threadId);
            }
        }

        static void KillTimer(int threadIdArg)
        {
            activeThreads.Remove(threadIdArg);
            activeTimers[threadIdArg].Dispose();
            activeTimers.Remove(threadIdArg);
        }
    }

    public class Worker
    {
        public KillTimerDelegate DoneCallback { get; set; }
        Random rnd = new Random();

        public void DoWork()
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + " started");
            Thread.Sleep(rnd.Next(0, 1000));
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + " finished normally");
            DoneCallback(Thread.CurrentThread.ManagedThreadId);
        }
    }
}
ebpower