在C#中五个线程之间的时间切片

时间:2014-11-14 04:30:11

标签: c# multithreading file-io job-scheduling round-robin

这是对程序应该做什么的描述。该程序应该创建一个文件和五个线程来写入该文件......

第一个线程应该从1到5写入该文件。 第二个线程应该从1到10写入。 第三个线程应该从1到15写入。 第四个线程应该从1到20写入。 第五个线程应该从1到25写入。

此外,应该实现一种算法,使每个线程打印2个数字并停止。下一个线程应该打印两个数字并停止。等等,直到所有线程完成打印数字。 这是我迄今为止开发的代码......

using System;
using System.IO;
using System.Threading;
using System.Collections;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    public static class OSAssignment
    {
        // First Thread Tasks...
        static void FirstThreadTasks(StreamWriter WritingBuffer)
        {
            for (int i = 1; i <= 5; i++)
            {
                if (i % 2 == 0)
                {
                    Console.WriteLine("[Thread1] " + i);
                    Thread.Sleep(i);
                }

                else
                {
                    Console.WriteLine("[Thread1] " + i);
                }
            }
        }

        // Second Thread Tasks...
        static void SecondThreadTasks(StreamWriter WritingBuffer)
        {
            for (int i = 1; i <= 10; i++)
            {
                if (i % 2 == 0)
                {
                    if (i == 10)
                        Console.WriteLine("[Thread2] " + i);

                    else
                    {
                        Console.WriteLine("[Thread2] " + i);
                        Thread.Sleep(i);
                    }
                }

                else
                {
                    Console.WriteLine("[Thread2] " + i);
                }
            }
        }

        // Third Thread Tasks..
        static void ThirdThreadTasks(StreamWriter WritingBuffer)
        {
            for (int i = 1; i <= 15; i++)
            {
                if (i % 2 == 0)
                {
                    Console.WriteLine("[Thread3] " + i);
                    Thread.Sleep(i);
                }

                else
                {
                    Console.WriteLine("[Thread3] " + i);
                }
            }
        }

        // Fourth Thread Tasks...
        static void FourthThreadTasks(StreamWriter WritingBuffer)
        {
            for (int i = 1; i <= 20; i++)
            {
                if (i % 2 == 0)
                {
                    if (i == 20)
                        Console.WriteLine("[Thread4] " + i);
                    else
                    {
                        Console.WriteLine("[Thread4] " + i);
                        Thread.Sleep(i);
                    }
                }

                else
                {
                    Console.WriteLine("[Thread4] " + i);
                }

            }
        }

        // Fifth Thread Tasks...
        static void FifthThreadTasks(StreamWriter WritingBuffer)
        {
            for (int i = 1; i <= 25; i++)
            {
                if (i % 2 == 0)
                {
                    Console.WriteLine("[Thread5] " + i);
                    Thread.Sleep(i);
                }

                else
                {
                    Console.WriteLine("[Thread5] " + i);
                }

            }
        }

        // Main Function...
        static void Main(string[] args)
        {
            FileStream File = new FileStream("output.txt", FileMode.Create, FileAccess.Write, FileShare.Write);
            StreamWriter Writer = new StreamWriter(File);
            Thread T1 = new Thread(() => FirstThreadTasks(Writer));
            Thread T2 = new Thread(() => SecondThreadTasks(Writer));
            Thread T3 = new Thread(() => ThirdThreadTasks(Writer));
            Thread T4 = new Thread(() => FourthThreadTasks(Writer));
            Thread T5 = new Thread(() => FifthThreadTasks(Writer));
            Console.WriteLine("Initiating Jobs...");
            T1.Start();
            T2.Start();
            T3.Start();
            T4.Start();
            T5.Start();
            Writer.Flush();
            Writer.Close();
            File.Close();
        }
    }
}

这是我面临的问题......

  1. 我无法弄清楚即使制作FileShare.Write,如何让5个线程同时写入同一个文件。因此,我只是决定暂时写入控制台并开发算法,并在控制台中查看它的行为。

  2. 每次运行程序时,输出都与之前略有不同。总是会发生一个线程在特定的迭代中仅打印其中一个数字,并在另一个线程完成当前迭代后继续输出第二个数字。

  3. 我有一个问题可能会以某种方式偏离轨道。如果我从主方法中移除了Console.WriteLine("Initiating Jobs...");,算法就不会像我在第2点中提到的那样表现。我真的无法弄清楚原因。

3 个答案:

答案 0 :(得分:4)

  1. 您的主要功能是在线程开始写入之前完成并关闭文件,因此您可以使用Thread.Join等待线程退出。另外,我建议对using个对象使用IDisposable语句。

  2. 当您想要在线程之间共享有限资源时,您需要一个锁定机制。线程调度不是确定性的。你已经启动了5个线程,并且在那时它不能保证哪个线程将首先运行。 lock将强制线程等待资源变为空闲。订单仍未确定,因此T3可能会在T2之前运行,除非您添加额外的逻辑/锁定以强制订单。

  3. 我没有看到行为上的太大差异,但是自由运行的线程会产生一些非常难以发现的错误,特别是与时序问题有关。

  4. 作为额外注释,我会避免使用Sleep作为同步线程的方法。

    为了在需要阻止所有其他线程的时候有效地获取一个线程,有一些方法可以实现这一点,例如lockMutexMonitor,{{ 1}}等我在这种情况下使用AutoResetEvent。您遇到的问题是每个线程需要知道它正在等待哪个线程,以便它可以等待正确的事件。

答案 1 :(得分:2)

请同时查看詹姆斯的回答。他指出了一个让我注意到的关键错误:你在编写器线程完成之前关闭了文件。考虑发一个新问题来询问如何解决这个问题,因为这个“问题”已经归结为三个问题。

  1. FileShare.Write告诉操作系统允许其他尝试打开文件进行写入。通常,这用于具有写入同一文件的多个进程的系统。在你的情况下,你有一个进程,它只打开一次文件,所以这个标志确实没有区别。这是工作的错误工具。
  2. 要协调多个线程之间的写入,您应该使用锁定。在类中添加一个新的静态字段:

    private static object synchronizer = new object();
    

    然后使用该对象上的锁定将每个写入操作包装在文件上:

    lock(synchronizer)
    {
        Console.WriteLine("[Thread1] " + i);    
    }
    

    当您使用控制台时,这没有任何区别,但我认为它将解决您写入文件时遇到的问题。

    说到这一点,从文件写入切换到控制台写入到回避文件问题是一个聪明的主意,所以对此赞不绝口。如果更好地实现该想法将是通过调用单个函数来替换所有写调用,例如, “WriteOutput(string)”,这样你就可以通过更改该函数中的一行来切换从文件到控制台的所有内容。

    然后你也可以将锁定放入该功能中。

    1. 线程化的东西不确定。它保证每个线程都会运行,但是没有关于排序的保证,线程将被中断,哪个线程会中断哪个线程等等。每次都是掷骰子。你只需要习惯它,或者如果真的对你的应用程序很重要的话,就不得不按照一定的顺序强迫事情发生。

    2. 我不知道这个。似乎这应该不重要。

答案 2 :(得分:0)

好的,我很晚才到这里,但从理论的角度来看,从多个线程到特定终点的I / O不可避免地充满了困难。

在上面的示例中,将输出排队到内存中结构几乎肯定会更快更安全,每个线程在执行此操作之前采用独占锁定,然后使用单独的线程输出到设备。