为什么线程增加执行时间c#

时间:2016-02-23 09:40:49

标签: c# multithreading

使用两个线程打印数字,这会增加时间,然后使用单循环打印我知道同步正在增加时间,但在我的代码中如何增加时间并停止线程打印重复的数字?还有呢?

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.5ms,循环是0.6ms

4 个答案:

答案 0 :(得分:4)

你有2个线程在相互等待,因为你锁定了线程之间的同步:

        lock (locker)
        {
            Console.WriteLine(Program.numbers[Program.count]);
            Program.count++;
        }

线程切换和等待会导致执行时间延长。

答案 1 :(得分:4)

多线程并不能保证性能的提升。

首先,多处理中真正重要的实际资源是并行处理单元的数量以及它们是否忙碌 - 并且 线程数。如果您的并行处理单元都忙,或者

the number of thread > number of parallel processing unit

然后制作更多线程将降低性能,增加它。

其次上下文切换因素。您拥有的线程越多,您执行上下文切换的频率就越高。因此,如果与上下文切换时间相比,Thread计算时间相对较低,那么使用多线程会有更差的性能。

第三,它还受 shared-resources (或同步)因素的影响:您的共享资源是否经常被访问多个线程 - 他们需要等待,从而导致更慢的执行。

你的情况中,它似乎是第三个​​案例,如@Peter所述。这是因为您拥有共享的全局count变量(非本地),并且每个线程都必须访问才能完成您的任务。也就是说,您的任务顺序本质上。这使得多个线程的执行时间比使用单个线程的执行时间更差。

对于这种情况,如果您的任务本质上不是顺序的(也就是说,可以在收集结果之前单独拆分并完成),那么您可以期望使用多线程获得更好的结果,然后您可以尝试查找{ {1}}执行你的任务。

例如:每个线程都有本地Parallel.For,并且在流程结束时将它们相加。

答案 2 :(得分:1)

正如其他答案所示,上下文切换和锁定需要时间来降低性能。因此,如果您希望更快地处理,则需要删除锁和共享资源。
除此之外,您的测试不适合探索多线程,因为除了显式锁之外, Console.WriteLine 中还有隐式锁(控制台也是共享资源)。

要提高性能,您需要删除锁定。

所以,例如,如果你运行两个线程,其中第一个线程只处理数字数组的一半(比如奇数),第二个线程处理后半部分(例如偶数)而不是console.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,ticks:2337320
for 2 take with lock take,ticks:2351632
对于没有锁定的2个线程,刻度:1176403

你可以看到带有两个没有锁定的线程的最后一个选项确实使你的处理速度提高了2倍。

答案 3 :(得分:1)

以下代码仍然是线程安全的,它更快,因为它是无锁的。我将A request to [http://localhost/login] failed. Received status code [302]. 关键字替换为lock

Interlocked.Increment