C# 中五个线程之间的时间切片
本文关键字:之间 线程 时间 切片 五个 | 更新日期: 2023-09-27 18:30:31
这是对程序应该做什么的描述。程序应该创建一个文件和五个线程来写入该文件......
第一个线程应从 1 写入该文件。第二个线程应从 1 写入到 10。第三个线程应从 1 写入 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();
}
}
}
这是我面临的问题...
即使制作
FileShare.Write
,我也无法弄清楚如何使 5 个线程同时写入同一个文件.所以我只是决定暂时写到控制台并开发算法,看看它首先在控制台中的行为。每次运行程序时,输出都与以前略有不同。线程在特定迭代中只打印其中一个数字,并在另一个线程完成其当前迭代后继续输出第二个数字。
我有一个问题可能有点偏离轨道。如果我从 main 方法中删除
Console.WriteLine("Initiating Jobs...");
,算法的行为将不会像我在第 2 点中提到的那样。我真的想不通为什么。
-
您的主要函数是在线程开始写入文件之前完成并关闭文件,因此您可以使用 Thread.Join 等待线程退出。此外,我建议对
IDisposable
对象使用using
语句。 -
当您想要在线程之间共享的资源有限时,您将需要一个锁定机制。线程调度不是确定性的。您已经启动了 5 个线程,此时无法保证哪个线程将首先运行。
lock
将强制线程等待资源变为可用。顺序仍未确定,因此 T3 可能会在 T2 之前运行,除非您添加其他逻辑/锁定来强制顺序。 -
我没有看到行为有太大区别,但自由运行的线程会产生一些很难找到的错误,尤其是与计时问题相关的错误。
作为额外的说明,我会避免使用Sleep
作为同步线程的一种方式。
为了有效地一次写入一个线程,您需要阻止所有其他线程,有几种方法可以做到这一点,例如lock
、Mutex
、Monitor
、AutoResetEvent
等。在这种情况下,我会使用AutoResetEvent
。然后您面临的问题是每个线程都需要知道它正在等待哪个线程,以便它可以等待正确的事件。
也请看詹姆斯的回答。他指出了一个逃脱我注意的关键错误:你在编写器线程完成之前关闭了文件。考虑发布一个新问题来询问如何解决这个问题,因为这个"问题"已经将三个问题合二为一。
- FileShare.Write 告知操作系统允许其他尝试打开文件进行写入。通常,这用于具有多个进程写入同一文件的系统。在您的情况下,您有一个进程,它只打开文件一次,因此此标志实际上没有区别。这是工作的错误工具。
若要协调多个线程之间的写入,应使用锁定。向类添加新的静态字段:
private static object synchronizer = new object();
然后用该对象的锁包装文件上的每个写入操作:
lock(synchronizer)
{
Console.WriteLine("[Thread1] " + i);
}
当您使用控制台时,这将没有区别,但我认为它将解决您在写入文件时遇到的问题。
说到这一点,从文件写入切换到控制台写入以避开文件问题是一个聪明的主意,所以为此点赞。该想法的更好实现方式是将所有写入调用替换为对单个函数的调用,例如"WriteOutput(string)",这样您就可以通过更改该函数中的一行来将所有内容从文件切换到控制台。
然后你也可以把锁放到那个功能中。
线程的东西不是确定性的。保证每个线程都会运行,但不能保证排序,线程何时中断,哪个线程会中断哪个线程,等等。每次都是掷骰子。你只需要习惯它,或者不遗余力地强制事情按一定的顺序发生,如果这对你的应用程序真的很重要。
我不知道这个。似乎这无关紧要。
好吧,我来得很晚,但从理论的角度来看,从多个线程到特定端点的 I/O 不可避免地令人担忧。
在上面的示例中,几乎可以肯定的是,将输出排队到内存结构中会更快、更安全,每个线程在这样做之前都采用独占锁,然后有一个单独的线程输出到设备。