为什么线程会增加执行时间 c#
本文关键字:执行时间 增加 线程 为什么 | 更新日期: 2023-09-27 18:35:45
使用两个线程打印数字会增加时间,然后使用单循环打印我知道同步正在增加时间,但是在我的代码中,我如何增加时间并停止线程打印重复的数字?有吗?
class Program
{
public static int count=0;
public static List<string> numbers = new List<string>();
public static int semaphore=0;
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
numbers.Add(i.ToString());
}
Console.WriteLine("Before start thread");
Thread tid1 = new Thread(new ThreadStart(MyThread.Thread1));
Thread tid2 = new Thread(new ThreadStart(MyThread.Thread1));
tid1.Start();
tid2.Start();
}
}
public class MyThread
{
public static object locker = new object();
public static void Thread1()
{
for (; Program.count < Program.numbers.Count;)
{
lock (locker)
{
Console.WriteLine(Program.numbers[Program.count]);
Program.count++;
}
}
}
}
这比线程更快,为什么?
foreach (var item in numbers)
{
Console.WriteLine(item);
}
线程的平均时间为 1.5 毫秒,循环时间为 0.6 毫秒
多线程不能保证性能的提高。
首先,在多处理中真正重要的实际资源是并行处理单元的数量以及它们是否繁忙 - 而不是线程的数量。如果您的并行处理单元都处于繁忙状态,或者如果
the number of thread > number of parallel processing unit
然后,创建更多线程会降低性能,而不是增加性能。
其次是上下文切换因素。您拥有的线程越多,执行上下文切换的频率可能就越高。因此,如果与上下文切换时间相比,Thread
计算时间相对较短,则多线程的性能会更差。
第三,它还受到共享资源(或同步)因素的影响:您的共享资源是否经常被多个线程访问 - 它们需要相互等待,从而导致执行速度变慢。
在您的情况下,这似乎是第三种情况,如@Peter所述。这是因为您有全局count
变量(不是局部变量),这些变量是共享的,并且必须由每个线程访问才能完成任务。也就是说,您有一个本质上是顺序的任务。这使得使用多个线程的执行时间比使用单个线程的执行时间更差。
对于这种情况,如果您的任务本质上不是顺序的(即,可以在收集结果之前拆分和独立完成),那么您可以尝试寻找Parallel.For
来执行您的任务,那么您可以使用多线程处理获得更好的结果。
例如:每个线程都有本地count
,在进程结束时将它们求和。
您有 2 个线程相互等待,因为您锁定了线程之间的同步:
lock (locker)
{
Console.WriteLine(Program.numbers[Program.count]);
Program.count++;
}
线程切换和等待会导致更长的执行时间。
正如其他答案所表明的那样,上下文切换和锁定需要时间降低性能。因此,如果您希望获得更快的处理速度,则需要删除锁和共享资源。
除此之外,您的测试不利于探索多线程,因为除了显式锁之外,Console.WriteLine 中还有隐式锁(控制台也是一个共享资源)。
要提高性能,您需要删除锁。
因此,例如,如果您运行两个线程,其中第一个线程仅处理数字数组的一半(例如奇数),第二个线程处理后半部分(例如偶数),而不是控制台。WriteLine 您执行不使用共享资源的操作,然后您将看到性能的提高。
考虑以下示例(我更改了您的代码):
class Program
{
public static int count = 0;
public static List<string> numbers = new List<string>();
public static System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
numbers.Add(i.ToString());
}
// First test - process nummbers in current thread
sw.Start();
foreach (var item in numbers)
{
DoSomethingWithTheNumber(item);
}
sw.Stop();
Console.WriteLine("foreach in main thread took, ticks: "+sw.ElapsedTicks);
// Second test - process nummbers in 2 threads with lock
Thread tid1 = new Thread(new ThreadStart(MyThread.Thread1));
Thread tid2 = new Thread(new ThreadStart(MyThread.Thread1));
sw.Reset();
sw.Start();
tid1.Start();
tid2.Start();
tid1.Join();
tid2.Join();
sw.Stop();
Console.WriteLine("for in 2 threads with lock took, ticks: " + sw.ElapsedTicks);
// Third test - process nummbers in 2 threads without lock
// first thread processes odd numbers, second processes odd numbers
Thread tid1A = new Thread(new ThreadStart(MyThreadWithoutLock.ThreadOddNumbers));
Thread tid2A = new Thread(new ThreadStart(MyThreadWithoutLock.ThreadEvenNumbers));
sw.Reset();
sw.Start();
tid1A.Start();
tid2A.Start();
tid1A.Join();
tid2A.Join();
sw.Stop();
Console.WriteLine("for in 2 threads without lock took, ticks: " + sw.ElapsedTicks);
Console.ReadKey();
}
public static void DoSomethingWithTheNumber(string number)
{
//Console.WriteLine(number);
Thread.Sleep(100);
}
public class MyThread
{
public static object locker = new object();
public static void Thread1()
{
for (; Program.count < Program.numbers.Count; )
{
lock (locker)
{
if(Program.count < Program.numbers.Count)
DoSomethingWithTheNumber(Program.numbers[Program.count]);
Program.count++;
}
}
}
}
public class MyThreadWithoutLock
{
public static void ThreadOddNumbers()
{
for (int i=1; i < Program.numbers.Count; i=i+2)
{
DoSomethingWithTheNumber(Program.numbers[i]);
}
}
public static void ThreadEvenNumbers()
{
for (int i = 0; i < Program.numbers.Count; i = i + 2)
{
DoSomethingWithTheNumber(Program.numbers[i]);
}
}
}
}
输出为:
主线程中的foreach,刻度:2337320
对于 2 个带锁的线程,刻度:2351632
对于 2 个没有锁定的线程,刻度:1176403
您可以看到,最后一个选项有两个线程而不锁定,确实可以让您的处理速度提高 2 倍。
以下代码虽然仍然是线程安全的,但它的速度更快,因为它是无锁的。我用Interlocked.Increment
替换了lock
关键字
using System;
using System.Collections.Generic;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
public static int count = 0;
public static List<string> numbers = new List<string>();
public static int semaphore = 0;
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
numbers.Add(i.ToString());
}
Console.WriteLine("Before start thread");
Thread tid1 = new Thread(new ThreadStart(MyThread.Thread1));
Thread tid2 = new Thread(new ThreadStart(MyThread.Thread1));
tid1.Start();
tid2.Start();
tid1.Join();
tid2.Join();
}
}
public class MyThread
{
public static void Thread1()
{
int nextIndex;
while ((nextIndex = Interlocked.Increment(ref Program.count)) <= Program.numbers.Count)
{
Console.WriteLine(Program.numbers[nextIndex - 1]);
}
}
}
}