出于好奇,我怎样才能建立一个多线程循环,从而保证从低到高的顺序,并且在循环完成后执行了Console.WriteLine("Done");
。我已经尝试了下面解释的三个案例。
Parallel.For(...)
如果要创建一个在C#中并行运行的for循环,则可以使用以下代码:
ParallelLoopResult result = Parallel.For(0, 10, i =>
{
Console.WriteLine($"{i}, task: {Task.CurrentId}, Thread: {Thread.CurrentThread.ManagedThreadId}";
Thread.Sleep(10);
}
Console.WriteLine($"Is completed: {result.IsCompleted}";
这将提供订单无法保证的下一个输出。如果再次运行代码,您将看到不同的结果。程序的运行顺序为0-2-4-6-8 -...,包含五个任务和六个线程。
0, task: 1, thread: 1
2, task: 2, thread: 3
4, task: 3, thread: 4
6, task: 4, thread: 5
8, task: 5, thread: 6
5, task: 3, thread: 4
7, task: 4, thread: 5
9, task: 5, thread: 6
3, task: 2, thread: 3
1, task: 1, thread: 1
Is completed: True
Thread
类我还尝试使用线程而不是Parallel
类。这是代码:
public static void Main(string[] args)
{
for (int i = 0; i < number; i++)
{
Thread tr = new Thread(() => Print(i));
tr.IsBackground = true;
tr.Start();
}
Console.WriteLine("Done");
}
private static void Print(int i)
{
Console.WriteLine(i);
Thread.Sleep(10);
}
现在这个代码的结果是,从低到高的顺序也没有保证顺序,并且在for循环结束之后写了字符串Done
但是并不总是保证,但是我&#39;在thread.Start();
之后放置Console.WriteLine("Done");
。
1
2
3
5
7
7
8
9
9
Done
就像我的第二次跑步一样:
2
3
3
4
5
6
7
8
Done
10
async
和await
我也会尝试这种方式,但我不知道如何为循环实现这种线程方式。
在所有情况下,我都没有得到我需要的结果。在第一种情况下,它不能保证被排序,在第二种情况下,在循环运行之前已经写入Done
。所以我的问题是如何运行for循环,在循环运行后始终保证顺序并写入Done
?
答案 0 :(得分:2)
您正在询问如何创建一个保证执行顺序的多线程循环。如前所述,你不能。但是,如果您想订购输出,可以这样做。这里有两个选项
保存结果直到它们全部完成(参见下面的伪代码):
在输出完成时订购输出。这比#1更难,但允许您更快地显示结果。
答案 1 :(得分:0)
虽然您不应该使用Threads进行此类顺序使用。你可以要求的是什么。
以下是您尝试实现目标的示例。它使用多个线程对象并保证顺序。但是,按顺序执行此操作可能会获得更好的性能。
public static void PrintDone()
{
Console.WriteLine("Done");
}
public static void Print(int i)
{
Console.WriteLine(i);
Thread.Sleep(10);
}
static void Main(string[] args)
{
List<Thread> threads = new List<Thread>();
for(int i = 0; i < 10; ++i)
{
int numCopy = i;
Thread th = new Thread(() => Print(numCopy));
threads.Add(th);
}
for (int i = 0; i < 10; ++i)
{
threads[i].Start();
threads[i].Join();
}
PrintDone();
Console.ReadLine();
}
因此,要点是创建一堆线程,然后按顺序启动它们(实际上并没有绕过那部分),然后直接加入主线程。将要发生的是主线程将启动&#34; subthread&#34;然后等待它完成,一旦它完成它将开始下一个线程,并重复直到它超出列表中的线程。
但是,如果您尝试并行化数字的打印并保证输出的顺序,那么您运气不好。仍然可能,但是你试图维持订单所带来的开销将会破坏并行化的目的。
这是代码的真正并行版本:
public static void PrintDone()
{
Console.WriteLine("Done");
}
public static void Print(int i, int[] threadStatus)
{
if(i != 0)
{
while(threadStatus[i-1] < 0)
{
Thread.Sleep(10);
}
}
Thread.Sleep(100);
threadStatus[i] = i;
Console.WriteLine(i);
}
static void Main(string[] args)
{
int[] threadStatus = Enumerable.Repeat(-1, 10).ToArray();
List<Thread> threads = new List<Thread>();
for(int i = 0; i < 10; ++i)
{
int numCopy = i;
Thread th = new Thread(() => Print(numCopy, threadStatus));
threads.Add(th);
}
for (int i = 0; i < 10; ++i)
{
threads[i].Start();
}
threads[9].Join();
PrintDone();
Console.ReadLine();
}
由于每个线程只能写入int数组的一部分并且读取是原子的,因此我们可以使用int数组来保持当前正在运行的所有线程的状态。
这基本上意味着在任何特定的&#34; print&#34;线程你可以检查它们是否已准备好在线程内打印,如果没有,你告诉它们在循环中睡觉,以便定期检查它们的状态。