【问题标题】:Thread timeout in c#c#中的线程超时
【发布时间】:2010-07-07 13:20:19
【问题描述】:

我是 C# 中的线程新手。 是否可以在不阻塞调用线程的情况下为线程设置超时(在 C# 3.5 中)?

如果不是,那么使用线程执行函数并在该函数中创建一个线程并加入它以克服这个主线程阻塞问题是否合乎逻辑?举例说明:

代替:

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

使用类似的东西:

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();
}

【问题讨论】:

标签: c# multithreading


【解决方案1】:

我检查过,最简单和最全面的方法是我在问题描述中提到的解决方案。中级线程可以轻松地等待第二个线程,而不会中断主线程;如果它在要求的时间内没有响应,它可以杀死第二个线程。这正是我所需要的。我用过,没问题。

【讨论】:

  • 我只是添加这个答案,以防有人遇到同样的问题并想看看我最后得到了什么。感谢大家的帮助。
【解决方案2】:

您可以为每个线程启动一个 System.Threading.Timer 并将线程的 ManagedThreadId 传递给它。保留活动线程及其计时器的字典,由 ManagedThreadId 键入。如果计时器到期,则使用传递的线程 ID 中止线程并终止其计时器。如果线程正常完成,则调用一个终止计时器的回调。这是一个简单的控制台示例:

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);
        }
    }
}

【讨论】:

【解决方案3】:

您可能还想看看ThreadPool.QueueUserWorkItem() (http://msdn.microsoft.com/en-us/library/kbf0f1ct.aspx),它为您做了很多事情。

正如 Brian 评论的那样,中止线程通常不是明智的做法,因为此时它可能正在做一些重要的事情。

【讨论】:

    【解决方案4】:

    WaitHandle.WaitOne()方法与middleObject方案。

    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();
    }
    

    但不确定未完成的线程会发生什么。

    【讨论】:

      【解决方案5】:
      【解决方案6】:

      最简单的做法是在主线程的安全点调用Thread.Join,并传递您希望等待连接发生的时间。

      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.
        }
      }
      

      【讨论】:

      • 避免使用 Thread.Abort 总是一个好主意。在您没有创建的线程上避免它甚至更好。如何在 .NET 中停止线程(以及为什么 Thread.Abort 是邪恶的)interact-sw.co.uk/iangblog/2004/11/12/cancellation Eric Lippert 的 Thread.Abort 的危险blogs.msdn.com/b/ericlippert/archive/2010/02/22/…
      • @Kiquenet 他已经提到“这是一个不安全的操作,所以作为最后的手段使用。”用于线程中止。相反,可以使用信号来安全地返回阻塞线程。
      【解决方案7】:

      “加入成员--> 阻塞调用线程直到线程终止,同时继续执行标准 COM 和 SendMessage 泵送。” MSDN 网站。

      thrd1.Join() 告诉调用线程等待直到 thrd1 完成。

      我最喜欢的解决方案是创建一个我能够控制线程执行的小类。

      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);
              }
         }
      

      【讨论】:

        【解决方案8】:

        我创建了一个 C# 类来执行超时线程,并且不会阻塞调用线程。

        using System;
        using System.Collections.Generic;
        using System.Threading;
        using System.Timers;
        
        namespace some_name_space
        {
            class TimedThread
            {
                private Thread thread;
                private static List<TimedThread> timedThreads = new List<TimedThread>();
                private Int32 timeout = 5000;
                private System.Timers.Timer timer;
                public TimedThread(ThreadStart start) { thread = new Thread(start); }        
        
                public int Timeout { get => timeout; set => timeout = value; }
                public System.Timers.Timer Timer { get => timer; }
                public Thread _Thread { get => thread; }
        
                public void run()
                {
                    timer = new System.Timers.Timer(timeout);
                    timer.Elapsed += OnTimedEvent;
                    timer.AutoReset = false;
                    timer.Enabled = true;
                    _Thread.Start();
                }
                private static void OnTimedEvent(Object source, ElapsedEventArgs e)
                {
                    TimedThread tt = timedThreads.Find(t => t.timer.Equals(source));
                    if (tt != null)
                        tt.thread.Abort("Timeout exception");
                }
            }
        }
        

        使用这个类:

        TimedThread tt = new TimedThread(SomeWorkToDo);
        tt.Timeout = 5000;
        tt.run();
        

        【讨论】:

        • .NET 不再支持Thread.Abort 方法。如果你在 .NET 5 或 .NET Core 上调用它,你会得到一个PlatformNotSupportedException
        猜你喜欢
        • 1970-01-01
        • 2011-01-12
        • 1970-01-01
        • 2015-10-30
        • 2011-06-21
        • 2019-11-30
        • 2018-04-01
        • 2017-03-13
        • 1970-01-01
        相关资源
        最近更新 更多