总是可以使用Task强制新线程吗?
我试图在每次调用Task.Factory.StartNew
时创建一个新线程。 问题是如何在不抛出exception的情况下运行代码:
static void Main(string[] args) { int firstThreadId = 0; Task.Factory.StartNew(() => firstThreadId = Thread.CurrentThread.ManagedThreadId); for (int i = 0; i { while (true) { Thread.Sleep(1000); if (firstThreadId == Thread.CurrentThread.ManagedThreadId) throw new Exception("The first thread is reused."); } }); } Console.Read(); }
编辑:新代码,如果你评论第一个语句没有问题。 但是,如果你拥有它,WOW,“Thread reused”消息将被写入控制台。 你能解释一下,因为我真的很困惑。
static void Main(string[] args) { ConcurrentDictionary startedThreads = new ConcurrentDictionary(); for (int i = 0; i { Task.Factory.StartNew(() => { startedThreads.AddOrUpdate(Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ManagedThreadId, (a, b) => b); }, TaskCreationOptions.LongRunning); for (int j = 0; j { while (true) { Thread.Sleep(10); if (startedThreads.ContainsKey(Thread.CurrentThread.ManagedThreadId)) Console.WriteLine("Thread reused"); } }, TaskCreationOptions.LongRunning); } }); } Console.Read(); }
如果在启动任务时指定TaskCreationOptions.LongRunning
提供调度程序的提示 ,默认调度程序将此提示作为为任务创建新线程的指示符。
这只是一个暗示 – 我不确定我会依赖它……但我没有看到任何使用默认调度程序的反例。
添加Jon Skeet的答案,如果您想保证每次都创建一个新线程,您可以编写自己的TaskScheduler
来创建一个新线程。
只需使用新的Thread()启动线程,然后使用Start()启动它们
static void Main(string[] args) { ConcurrentDictionary startedThreads = new ConcurrentDictionary(); for (int i = 0; i < 10; i++) { new Thread(() => { new Thread(() => { startedThreads.AddOrUpdate(Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ManagedThreadId, (a, b) => b); }).Start(); for (int j = 0; j < 100; j++) { new Thread(() => { while (true) { Thread.Sleep(10); if (startedThreads.ContainsKey(Thread.CurrentThread.ManagedThreadId)) Console.WriteLine("Thread reused"); } }).Start(); } }).Start(); } Console.Read(); }
任务应该由调度程序管理。 Tasks的整个想法是运行时将决定何时需要新线程。 另一方面,如果你确实需要不同的线程,那么代码中的其他东西很可能是错误的,例如对Thread.Sleep()或线程本地存储的过度依赖。
正如所指出的,您可以创建自己的TaskScheduler并使用任务来创建线程但是为什么要使用Tasks来开始?
您好,谢谢大家的答案。 你们都得到了+1。 所有建议的解决方案都不适合我的情况。 问题是当你睡眠一个线程时,它会在某个时间点被重用。 以上人士建议:
- 使用LongRunning =>如果您有嵌套/子任务,这将不起作用
- 自定义任务调度程序=>我试着写自己的,也试过这个ThreadPerTaskScheduler ,这也不起作用。
- 使用纯线程=>仍然失败…
- 你也可以在Multithreading.Scheduler github上查看这个项目
我的解决方案
我不喜欢它,但它有效。 基本上我阻止线程,因此无法重用。 贝娄是扩展方法和工作实例。 再次谢谢你。
https://gist.github.com/4150635
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace ConsoleApplication { public static class ThreadExtensions { /// /// Blocks the current thread for a period of time so that the thread cannot be reused by the threadpool. /// public static void Block(this Thread thread, int millisecondsTimeout) { new WakeSleepClass(millisecondsTimeout).SleepThread(); } /// /// Blocks the current thread so that the thread cannot be reused by the threadpool. /// public static void Block(this Thread thread) { new WakeSleepClass().SleepThread(); } /// /// Blocks the current thread for a period of time so that the thread cannot be reused by the threadpool. /// public static void Block(this Thread thread, TimeSpan timeout) { new WakeSleepClass(timeout).SleepThread(); } class WakeSleepClass { bool locked = true; readonly TimerDisposer timerDisposer = new TimerDisposer(); public WakeSleepClass(int sleepTime) { var timer = new Timer(WakeThread, timerDisposer, sleepTime, sleepTime); timerDisposer.InternalTimer = timer; } public WakeSleepClass(TimeSpan sleepTime) { var timer = new Timer(WakeThread, timerDisposer, sleepTime, sleepTime); timerDisposer.InternalTimer = timer; } public WakeSleepClass() { var timer = new Timer(WakeThread, timerDisposer, Timeout.Infinite, Timeout.Infinite); timerDisposer.InternalTimer = timer; } public void SleepThread() { while (locked) lock (timerDisposer) Monitor.Wait(timerDisposer); locked = true; } public void WakeThread(object key) { locked = false; lock (key) Monitor.Pulse(key); ((TimerDisposer)key).InternalTimer.Dispose(); } class TimerDisposer { public Timer InternalTimer { get; set; } } } } class Program { private static readonly Queue tokenSourceQueue = new Queue (); static void Main(string[] args) { CancellationTokenSource tokenSource = new CancellationTokenSource(); tokenSourceQueue.Enqueue(tokenSource); ConcurrentDictionary startedThreads = new ConcurrentDictionary(); for (int i = 0; i < 10; i++) { Thread.Sleep(1000); Task.Factory.StartNew(() => { startedThreads.AddOrUpdate(Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ManagedThreadId, (a, b) => b); for (int j = 0; j < 50; j++) Task.Factory.StartNew(() => startedThreads.AddOrUpdate(Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ManagedThreadId, (a, b) => b)); for (int j = 0; j < 50; j++) { Task.Factory.StartNew(() => { while (!tokenSource.Token.IsCancellationRequested) { if (startedThreads.ContainsKey(Thread.CurrentThread.ManagedThreadId)) Console.WriteLine("Thread reused"); Thread.CurrentThread.Block(10); if (startedThreads.ContainsKey(Thread.CurrentThread.ManagedThreadId)) Console.WriteLine("Thread reused"); } }, tokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default) .ContinueWith(task => { WriteExceptions(task.Exception); Console.WriteLine("-----------------------------"); }, TaskContinuationOptions.OnlyOnFaulted); } Thread.CurrentThread.Block(); }, tokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default) .ContinueWith(task => { WriteExceptions(task.Exception); Console.WriteLine("-----------------------------"); }, TaskContinuationOptions.OnlyOnFaulted); } Console.Read(); } private static void WriteExceptions(Exception ex) { Console.WriteLine(ex.Message); if (ex.InnerException != null) WriteExceptions(ex.InnerException); } } }