总是可以使用Task强制新线程吗?

myn*_*kow 9 c# multithreading

我正在尝试每次Task.Factory.StartNew调用时创建一个新线程.问题是如何在不抛出异常的情况下运行代码:

static void Main(string[] args)
        {
            int firstThreadId = 0;

            Task.Factory.StartNew(() => firstThreadId = Thread.CurrentThread.ManagedThreadId);

            for (int i = 0; i < 100; i++)
            {
                Task.Factory.StartNew(() =>
                {
                    while (true)
                    {
                        Thread.Sleep(1000);
                        if (firstThreadId == Thread.CurrentThread.ManagedThreadId)
                            throw new Exception("The first thread is reused.");
                    }
                });
            }
            Console.Read();
        }
Run Code Online (Sandbox Code Playgroud)

编辑:新代码,如果你评论第一个for语句没有问题.但是,如果你拥有它,WOW,会将"Thread reused"消息写入控制台.你能解释一下,因为我真的很困惑.

static void Main(string[] args)
        {
            ConcurrentDictionary<int, int> startedThreads = new ConcurrentDictionary<int, int>();

            for (int i = 0; i < 10; i++)
            {
                Task.Factory.StartNew(() =>
                {
                    Task.Factory.StartNew(() =>
                    {
                        startedThreads.AddOrUpdate(Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ManagedThreadId, (a, b) => b);
                    }, TaskCreationOptions.LongRunning);

                    for (int j = 0; j < 100; j++)
                    {
                        Task.Factory.StartNew(() =>
                        {
                            while (true)
                            {
                                Thread.Sleep(10);
                                if (startedThreads.ContainsKey(Thread.CurrentThread.ManagedThreadId))
                                    Console.WriteLine("Thread reused");
                            }
                        }, TaskCreationOptions.LongRunning);
                    }
                });
            }

            Console.Read();
        }
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 19

如果指定TaskCreationOptions.LongRunning何时启动任务,则会向调度程序提供提示,默认调度程序将此提示作为为任务创建新线程的指示符.

这只是一个提示 - 我不确定我会依赖它......但我没有看到任何使用默认调度程序的反例.


SLa*_*aks 6

添加到 Jon Skeet 的答案中,如果您想保证每次都TaskScheduler创建一个新线程,您可以编写自己的创建新线程的线程。


myn*_*kow 3

您好,谢谢大家的回答。你们都得到了+1。所有建议的解决方案都不适合我的情况。问题是当你休眠一个线程时,它会在某个时间点被重用。上面的人建议:

我的解决方案

我不喜欢它,但它有效。基本上我阻塞了线程,所以它不能被重用。以下是扩展方法和工作示例。再次谢谢你。

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
    {
        /// <summary>
        /// Blocks the current thread for a period of time so that the thread cannot be reused by the threadpool.
        /// </summary>
        public static void Block(this Thread thread, int millisecondsTimeout)
        {
            new WakeSleepClass(millisecondsTimeout).SleepThread();
        }

        /// <summary>
        /// Blocks the current thread so that the thread cannot be reused by the threadpool.
        /// </summary>
        public static void Block(this Thread thread)
        {
            new WakeSleepClass().SleepThread();
        }

        /// <summary>
        /// Blocks the current thread for a period of time so that the thread cannot be reused by the threadpool.
        /// </summary>
        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<CancellationTokenSource> tokenSourceQueue = new Queue<CancellationTokenSource>();
        static void Main(string[] args)
        {
            CancellationTokenSource tokenSource = new CancellationTokenSource();
            tokenSourceQueue.Enqueue(tokenSource);

            ConcurrentDictionary<int, int> startedThreads = new ConcurrentDictionary<int, int>();
            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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)