具有序列的多线程

时间:2012-07-27 02:54:43

标签: c# multithreading sequence

我有一个主要任务是产生线程来做一些工作。工作完成后,它将写入控制台。

我的问题是,稍后创建的某些线程将比先前创建的线程完成得更快。但是我需要以与创建线程完全相同的顺序完成对控制台的写入。

因此,如果一个线程已经完成了它的任务,而一些早期的线程没有,那么它必须等到那些早期的线程也完成。

    public class DoRead
    {
        public DoRead()
        {
        }

        private void StartReading()
        {
            int i = 1;

            while (i < 10000)
            {
                Runner r = new Runner(i, "Work" + i.ToString());
                r.StartThread();
                i += 1;
            }
        }
    }

    internal class Runner : System.IDisposable
    {
        int _count;
        string _work = "";

        public Runner(int Count, string Work)
        {
            _count = Count;
            _work = Work;
        }

        public void StartThread()
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(runThreadInPool), this);
        }

        public static void runThreadInPool(object obj)
        {
            ((Runner)obj).run();
        }

        public void run()
        {
            try
            {
                Random r = new Random();
                int num = r.Next(1000, 2000);

                DateTime end = DateTime.Now.AddMilliseconds(num);
                while (end > DateTime.Now)
                {
                }

                Console.WriteLine(_count.ToString() + " : Done!");
            }
            catch
            {
            }
            finally
            {
                 _work = null;
            }
        }

        public void Dispose()
        {
             this._work = null;
        }

    }

3 个答案:

答案 0 :(得分:1)

可能有一种比我使用的更简单的方法,(我习惯于.Net 4.0)。

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

namespace ConsoleApplication5
{
    class Program
    {
        public static readonly int numOfTasks = 100;

        public static int numTasksLeft = numOfTasks;

        public static readonly object TaskDecrementLock = new object();

        static void Main(string[] args)
        {
            DoRead dr = new DoRead();

            dr.StartReading();

            int tmpNumTasks = numTasksLeft;

            while ( tmpNumTasks > 0 )
            {
                Thread.Sleep(1000);
                tmpNumTasks = numTasksLeft;
            }


            List<string> strings = new List<string>();

            lock( DoRead.locker )
            {
                for (int i = 1; i <= Program.numOfTasks; i++)
                {
                    strings.Add( DoRead.dicto[i] );
                }
            }

            foreach (string s in strings)
            {
                Console.WriteLine(s);
            }

            Console.ReadLine();
        }

        public class DoRead
        {

            public static readonly object locker = new object();

            public static Dictionary<int, string> dicto = new Dictionary<int, string>();

            public DoRead()
            {
            }

            public void StartReading()
            {
                int i = 1;

                while (i <= Program.numOfTasks )
                {
                    Runner r = new Runner(i, "Work" + i.ToString());
                    r.StartThread();
                    i += 1;
                }


            }
        }

        internal class Runner : System.IDisposable
        {
            int _count;
            string _work = "";

            public Runner(int Count, string Work)
            {
                _count = Count;
                _work = Work;
            }

            public void StartThread()
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(runThreadInPool), this);
            }

            public static void runThreadInPool(object obj)
            {
                Runner theRunner = ((Runner)obj);
                string theString = theRunner.run();

                lock (DoRead.locker)
                {
                    DoRead.dicto.Add( theRunner._count, theString);
                }

                lock (Program.TaskDecrementLock)
                {
                    Program.numTasksLeft--;
                }
            }

            public string run()
            {
                try
                {
                    Random r = new Random();
                    int num = r.Next(1000, 2000);

                    Thread.Sleep(num);

                    string theString = _count.ToString() + " : Done!";

                    return theString;

                }
                catch
                {
                }
                finally
                {
                    _work = null;
                }

                return "";
            }

            public void Dispose()
            {
                this._work = null;
            }

        }
    }
}

基本上,我将您要从每个任务打印的字符串存储到索引为任务#的字典中。 (我使用锁来安全地访问字典。)

接下来,为了让主程序等到所有后台线程完成,我使用了对NumTasksLeft变量的另一个锁定访问。

我在Runner的回调中添加了东西。

使用busy循环是不好的做法,因此我将其更改为Thread.Sleep(num)语句。

只需将numOfTasks更改为10000以匹配您的示例。

我按顺序将返回字符串拉出字典,然后将其打印到屏幕上。

我确信你可以重构这个以移动或以其他方式处理全局变量,但这可行。

另外,你可能已经注意到我没有在命令中使用锁

 tmpNumTasks = numTasksLeft;

这是线程安全的,因为numTasksLeft是一个在32位计算机上以原子方式读取的int。

答案 1 :(得分:0)

我对C#了解不多,但多线程的整个想法是你有多个线程独立执行,你永远不知道哪个先前会完成(你不应该期望早期的线程更早结束) )。

一种解决方法是,在处理线程中写出完成消息,让处理线程在某处设置一个标志(可能是一个没有元素的列表=没有生成的线程),并且有一个单独的线程打印完成消息基于该列表中的标志,并报告直到前一个标志连续“完成”的位置。

老实说,我觉得打印完成这样的消息并不合理。我认为改变设计更好地拥有这种毫无意义的“特征”。

答案 2 :(得分:0)

通常情况下,这些要求会增加序号,就像您已经完成的那样。

通常,处理线程的输出通过包含所有无序结果对象的列表(或字典)的过滤器对象提供,“保留它们”直到所有结果具有较低的顺序 - 数字已经进入。再次,类似于你已经做过的事情。

什么是不必要的是任何类型的sleep()循环。工作线程本身可以操作过滤器对象(可以锁定),或者工作线程可以将生产者 - 消费者 - 将结果排队到操作无序过滤器的“输出线程”。

此方案适用于池化工作线程,即。那些没有持续创造/终止/破坏开销的人。