2014-06-21 33 views
0

我已经得到了以下多线程代码来计算欧拉数。我是多线程编程的新手,也许我错过了一些东西。出于某种原因,countdown.Wait()并不是等待所有线程,totalSum几乎每次都是不同的。它看起来像跳过了一些中间值。CountdownEvent不等待所有线程发信号

public static class Program 
{ 
    private static int elementsCount = 500; 
    private static int threadsCount = 20; 
    private static string outputFileName = "defaultFileName.txt"; 
    private static bool isInQuietMode = false; 

    private static BigRational totalSum = new BigRational(0.0m); 

    private static CountdownEvent countDown = new CountdownEvent(threadsCount); 
    private static Object locker = new Object(); 

    private static void Main(string[] args) 
    { 
     Stopwatch stopwatch = new Stopwatch(); 
     stopwatch.Start(); 

     for (int threadIndex = 0; threadIndex < threadsCount; threadIndex++) 
     { 
      ThreadPool.QueueUserWorkItem(new WaitCallback(CalculateEulerNumber), threadIndex); 
     } 

     countDown.Wait(); 

     File.WriteAllText(outputFileName, "Euler's number: " + totalSum); 

     stopwatch.Stop(); 

     Console.WriteLine("Result: "); 
     Console.WriteLine("Total time elapsed - " + stopwatch.Elapsed); 
     if (!isInQuietMode) 
     { 
      Console.WriteLine("Euler's number - " + totalSum); 
     } 
    } 

    private static void CalculateEulerNumber(object threadIndexObject) 
    { 
     Stopwatch stopwatch = new Stopwatch(); 
     stopwatch.Start(); 

     int threadIndex = Convert.ToInt32(threadIndexObject); 

     BigRational sum = new BigRational(0.0m); 

     for (int k = threadIndex; k < elementsCount; k += threadsCount) 
     { 
      BigRational numerator = BigRational.Pow((3 * k), 2) + 1; 
      BigRational denominator = Factorial(3 * k); 

      sum += BigRational.Divide(numerator, denominator); 
     } 

     totalSum = BigRational.Add(totalSum, sum); 

     stopwatch.Stop(); 

     lock (locker) 
     { 
      int threadNumber = threadIndex + 1; 

      Console.WriteLine("Тhread " + threadNumber + ": "); 
      Console.WriteLine("Time elapsed - " + stopwatch.Elapsed); 

      if (!isInQuietMode) 
      { 
       Console.WriteLine("Intermediate sum - " + sum.ToDecimalString(40)); 
      } 

      Console.WriteLine(); 
     } 

     countDown.Signal(); 
    } 

    private static BigRational Factorial(int n) 
    { 
     BigRational factorial = 1; 

     for (int i = 1; i <= n; i++) 
     { 
      factorial *= i; 
     } 

     return factorial; 
    } 
} 
+1

您正在以敏锐的方式写入共享变量。 – usr

+0

是的,这是问题,谢谢。但是我仍然不明白为什么竞争条件在这里有一些影响,因为增加中间和的次序并不重要。 – user3763152

+0

你假设这条线是原子的,但它不是。它由许多原子操作组成,它们总的来说不是原子的。 – usr

回答

0

@usr取得了良好的一点:你更好地使用ConcurrentStack<T>ConcurrentQueue<T>http://msdn.microsoft.com/en-us/library/system.collections.concurrent%28v=vs.110%29.aspx为详细。 此外,最好使用Task.Factory实现您的算法,如Alexandra Rusinahttp://blogs.msdn.com/b/csharpfaq/archive/2010/06/01/parallel-programming-in-net-framework-4-getting-started.aspx中解释的那样。由于每所提到的资源,您的解决方案可能看起来像下面的东西(给你的总体思路)

public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

ConcurrentStack<int> cs = new ConcurrentStack<int>(); 

     public static double YourFunction(int SomeNumber) 
     { 
      // computation of result 
      return result; 
     } 

     private void start_Click(object sender, RoutedEventArgs e) 
     { 
      textBlock1.Text = ""; 
      label1.Content = "Milliseconds: "; 

      var watch = Stopwatch.StartNew(); 
      List<Task> tasks = new List<Task>(); 
      for (int i = 2; i < 20; i++) 
      { 
       int j = i; 
       var t = Task.Factory.StartNew(() => 
       { 
        int result = YourFunctiopn(j); 
        this.Dispatcher.BeginInvoke(new Action(() => 
         cs.Add(result)) 
        , null); 
       }); 
       tasks.Add(t); 
      } 

      Task.Factory.ContinueWhenAll(tasks.ToArray(), 
        result => 
        { 
         var time = watch.ElapsedMilliseconds; 
         this.Dispatcher.BeginInvoke(new Action(() => 
          label1.Content += time.ToString())); 
        }); 

     } 
    } 

希望这会有所帮助。 Rgds,

0

您正在使用CountDownEvent错误.CountDownEvent用于信号发送,并且在当前程序中不需要此。你可以用任务做到这一点:

public class Class1 
{ 
    private static int elementsCount = 500; 
    private static int threadsCount = 20; 
    private static string outputFileName = "defaultFileName.txt"; 
    private static bool isInQuietMode = false; 
    private static BigRational totalSum = new BigRational(0.0m); 

    public static void Main1(string[] args) 
    { 
     Stopwatch stopwatch = new Stopwatch(); 
     stopwatch.Start(); 
     List<Task<BigRational>> tasks = new List<Task<BigRational>>(); 

     //Create the tasks 
     for (int threadIndex = 0; threadIndex < threadsCount; threadIndex++) 
     { 
      Task<BigRational> task = new Task<BigRational>((data)=> 
      { 
       return CalculateEulerNumber(data); 

      },threadIndex); 
      tasks.Add(task); 
     } 
     foreach (var task in tasks) 
     { 
      task.Start(); 
     } 
     //Wait for tasks 
     Task.WaitAll(tasks.ToArray()); 

     //Add the results 
     foreach (var task in tasks) 
     { 
      totalSum = BigRational.Add(totalSum, task.Result); 
     } 
     File.WriteAllText(outputFileName, "Euler's number: " + totalSum); 

     stopwatch.Stop(); 

     Console.WriteLine("Result: "); 
     Console.WriteLine("Total time elapsed - " + stopwatch.Elapsed); 
     if (!isInQuietMode) 
     { 
      Console.WriteLine("Euler's number - " + totalSum); 
     } 
    } 

    private static BigRational CalculateEulerNumber(object threadIndexObject) 
    { 
     Stopwatch stopwatch = new Stopwatch(); 
     stopwatch.Start(); 

     int threadIndex = Convert.ToInt32(threadIndexObject); 

     BigRational sum = new BigRational(0.0m); 

     for (int k = threadIndex; k < elementsCount; k += threadsCount) 
     { 
      BigRational numerator = BigRational.Pow((3 * k), 2) + 1; 
      BigRational denominator = Factorial(3 * k); 

      sum += BigRational.Divide(numerator, denominator); 
     } 
     stopwatch.Stop(); 
     int threadNumber = threadIndex + 1; 

      Console.WriteLine("Тhread " + threadNumber + ": "); 
      Console.WriteLine("Time elapsed - " + stopwatch.Elapsed); 

      if (!isInQuietMode) 
      { 
       Console.WriteLine("Intermediate sum - " + sum.ToString()); 
      } 

      Console.WriteLine(); 
     return sum; 
    } 

    private static BigRational Factorial(int n) 
    { 
     BigRational factorial = 1; 

     for (int i = 1; i <= n; i++) 
     { 
      factorial *= i; 
     } 

     return factorial; 
    } 
} 

因此,创建任务,每个任务可以seperately运行和返回个体之和。然后您可以添加结果来创建总和。也不需要锁。

+0

由于Console.WriteLine在每个线程中都使用锁定,所以输出线路变得杂乱无章 – user3763152

+0

您可以使用stringbuilder添加所有输出,然后执行控制台。的WriteLine – Avneesh