2016-02-23 2 views
-3

Использование двух потоков для печати чисел, которые увеличивают время, а затем используют одиночный цикл для печати. ​​Я знаю, что синхронизация увеличивает время, но в моем коде как увеличить время и остановить потоки для печати повторяющихся номеров? Там в любом случае ?Почему потоковое увеличение времени выполнения C#

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.6ms

+0

Я знаю, что время растет в связи с ожиданием локона ресурса. В моем случае я должен использовать синхронизацию. Есть ли способ увеличить время выполнения? без синхронизации в моем коде? – phpnet

+0

Ну, больше потоков, чем процессорные ядра, увеличивает время выполнения из-за переключения контекста, но не настолько. Если вы хотите избежать синхронизации, вызванные задержки, просто не используйте код потока, который требует синхронизации. Создайте приложение таким образом, чтобы все распараллеливаемые выполнялись без синхронизации. –

+0

В моей проблеме вы можете придумать способ сделать это параллельно? – phpnet

ответ

3

У вас есть 2 темы, которые ждут друг от друга, потому что вы блокировки синхронизации между потоками:

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

Нить переключатели и ожидания приводят к увеличению времени выполнения.

+0

Я знаю, что @Peter, но как избежать синхронизации? – phpnet

+0

Вы должны использовать только локальные переменные в своей функции MyThread вместо статики, которую вы используете сейчас. – Peter

4

Многопоточность не гарантирует увеличения производительности.

Во-первых, фактические ресурсы, которые действительно имеют значение в многопроцессорной обработки является количество параллельного блока обработки и являются ли они заняты или не - и не числа нитей. Если параллельные блоки обработки все заняты, или если

the number of thread > number of parallel processing unit 

затем сделать более нить снижение производительность, не увеличения.

Во-вторых, является переключателем контекста . Чем больше потоков у вас есть, тем чаще вы делаете свои переключатели . Поэтому, если ваше время вычисления Thread относительно невелико по сравнению с временем переключения контекста, то у вас есть хуже с многопоточным.

И третьих, она также зависит от разделяемых ресурсов (или синхронизацию) фактора: ли ваши общие ресурсы часто доступ к нескольким потоков - где они должны ждать для одного другой, в результате чего медленнее исполнение.

В вашем случае, это, кажется, третий случае, как уже упоминалось @Peter. Это связано с тем, что у вас есть глобальные count переменные (не локальные), которые являются общими, и должен быть доступен каждой отдельной нитью для выполнения вашей задачи. То есть у вас есть задание последовательное от природы. Это делает ваше время выполнения несколькими потоками хуже, чем время выполнения с помощью одного потока.

Для этого случая вы можете рассчитывать на лучший результат при многопоточности, если у вас есть задача, которая не является последовательной по своей природе (то есть может быть разделена и сделана независимо до сбора результатов), тогда вы можете попытаться найти Parallel.For для выполнения ваших задач.

Например: у вас есть локальный count для каждой темы, и в конце процесса вы их суммируете.

1

Как и другие ответы, переключение контекста и блокировка требуют снижения производительности. Итак, если вы хотите получить более быструю обработку, вам нужно удалить блокировки и общие ресурсы.
В дополнение к этому, ваш тест не подходит для изучения многопоточности, поскольку в дополнение к вашей явной блокировке также существует неявный замок внутри 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]); 
      } 
     } 
    } 
} 

Выход:
Еогеасп в основном потоке взяли, клещи: 2337320
для в 2-х нитей с замком взял, клещи: 2351632
для в 2 потоках без блокировки, тиков: 1176403

Вы можете видеть, что последний вариант с двумя потоками без блокировки действительно дает вам в 2 раза большую скорость обработки.

1

Следующий код в то же время безопасный поток, это быстрее, потому что он заблокирован. Я заменил lock ключевое слово с Interlocked.Increment

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]); 
      } 
     } 
    } 
} 
Смежные вопросы