2015-01-07 117 views
0

我有一個應用程序,帶有一個調用長時間運行時間函數的啓動按鈕。爲了增加一個停止按鈕,我爲此添加了一個線程,以避免UI凍結,並能夠隨時停止處理。提高線程性能

沒有線程的代碼平均需要12分鐘才能完成處理,但通過線程的方式,我在 以下的方式需要4倍多。下面顯示的是啓動按鈕的代碼,其中稱爲函數「LongRunningFunction」。函數 需要一個字符串參數才能工作「LongRunningFunction(Somestring)」。

我已經用Task.Run和Task.Factory.StartNew進行了測試,但是兩種方法都發生了相同的情況。

是否有另一種方法來設置一個線程爲我的情況,不會影響太多的性能?

public partial class Form1 : Form 
{ 
    CancellationTokenSource cts = new CancellationTokenSource(); // Create the token source. 
    public Form1() 
    { 
     InitializeComponent(); 
    } 
    private void Start_Click(object sender, EventArgs e) 
    { 
     if (cts != null) 
     { 
      cts.Cancel(); 
     } 
     cts = new CancellationTokenSource(); 
     Task.Run(()=> LongRunningFunction(Somestring, cts.Token), cts.Token);    
     //Task.Factory.StartNew(() => LongRunningFunction(Somestring, cts.Token), cts.Token, TaskCreationOptions.None, TaskScheduler.Default); 
    } 
    private void Stop_Click(object sender, EventArgs e) 
    { 
     if (cts != null) 
     { 
      cts.Cancel(); 
      cts = null; 
      MessageBox.Show("Processing cancelled"); 
     } 
    } 
    public void LongRunningFunction(string String, CancellationToken token) 
    { 
     //Long running processing 
     //... 
     MessageBox.Show("Processing finished"); 
    } 
} 

更新: 唯一的東西我改變的是我聲明功能,並增加了的方式,如果while循環 是在函數內部內聲明。如下所示:

  • 已添加CancelationToken以便能夠在按下停止按鈕時停止處理。

無螺紋我聲明函數是這樣的:

public void LongRunningFunction(string String) 
{ 
    while (condition) 
    { 
     //My code within While loop 
    } 
    MessageBox.Show("Processing finished"); 
} 

與線程我這樣定義函數:

public void LongRunningFunction(string String, CancellationToken token) 
{ 
    while (condition) 
    { 
     if (token.IsCancellationRequested) 
     { 
      break; 
     }  
     //My code within While loop 
    } 
    if (!token.IsCancellationRequested) 
    { 
     MessageBox.Show("Processing finished"); 
    }  
} 

UPDATE2: 內LongRunningFunction()被調用另一個打印行的功能。就像下面。

public void LongRunningFunction(string fileName, CancellationToken token) 
    { 
     StreamWriter writer = new StreamWriter(@outputfile, true, Encoding.UTF8, 4096); 

     using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open))) 
     { 
      List<byte> buffer = new List<byte>(); 
      List<string> buffer1 = new List<string>(); 

      SoapHexBinary hex = new SoapHexBinary(); 

      while (chunk.Length > 0) 
      { 
       if (token.IsCancellationRequested) // ### For Cancel Thread ### 
       { 
        break; 
       } // ### For Cancel Thread ###  

        chunk = reader.ReadBytes(1024); 

        foreach (byte data in chunk) 
        { 
         if (somecondition) 
         { 
          buffer.Add(data);       
         } 
         else if (other condition) 
         { 
          buffer.Add(data); 
          PrintFunction(buffer, hex, outputfile, writer); // Print Line 
         } 
         else if (some other condition) 
         { 
          buffer.Add(data); 
         } 
        }     
      }   
      if (!token.IsCancellationRequested) 
      { 
       MessageBox.Show("Processing finished"); 
      } 

     } 

     if (writer != null) 
     { 
      writer.Dispose(); 
      writer.Close(); 
     } 
    }  
    private void PrintFunction(List<byte> buffer, SoapHexBinary hex, string outputfile, StreamWriter writer) 
    { 
      if (buffer.Count > 0) 
      { 
       if (buffer.Count >= lowlimit) 
       { 
        hex.Value = buffer.ToArray(); 
        string Register = hex.ToString(); 

        Regex pattern1 = new Regex(@"some pattern"); 

        if (pattern1.IsMatch(Register)) 
        { 
         Match l1 = Regex.Match(Register, @"somepattern", RegexOptions.IgnoreCase | RegexOptions.Compiled); 
         writer.Write("{0}|{1}|{2}", Convert.ToInt32(l1.Groups[1].ToString(), 16), l1.Groups[2].Value, l1.Groups[3].Value); 
         Match l2 = Regex.Match(Register, @"otherpattern", RegexOptions.IgnoreCase | RegexOptions.Compiled); 
         if (l2.Success) 
         { 
          foreach (Match m in Regex.Matches(l2.Groups[2].ToString(), pattern2, RegexOptions.IgnoreCase | RegexOptions.Compiled)) 
          { 
           //Some foreach code 
          } 
          foreach (Match x in Regex.Matches(var, @"pattern")) 
          { 
           //come code 
          } 
          writer.WriteLine("," + String.Join(",", var1)); 
         } 
         else 
         { 
          writer.WriteLine(); 
         } 
        } 
       } 
      } 
      buffer.Clear(); 
    } 

UPDATE3: 嗨bebosh,

我還是有疑問如何在我的功能應用,你的方式在您的示例功能定義委託。

我的功能看起來是這樣的:

public void LongRunningFunction(string fileName) 
{ 
    using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open))) 
    { 
     // some code 
    } 
}  

這可能是這樣或如何?:

private void LongRunningFunction(string fileName) 
{ 
    MethodInvoker action = delegate 
    { 
     using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open))) 
     { 
      // some code 
     } 
    }; 
} 
+2

在一個額外的線程而不是UI線程上運行代碼不應該降低性能4倍(它也不應該真的提高性能,因爲完成相同的工作,給予或處理幾個窗口消息)。你還有什麼改變?你如何處理原始代碼沒有的'CancellationToken'? – CodeCaster

+0

如果操作需要12分鐘,您應該使用'TaskCreationOptions.LongRunning',而不是那會導致您看到的4倍放緩。 – Servy

+0

您的LongRunningFunction()是否有任何機會(通過使用分派器或任何其他機制)更新UI?如果沒有,我看不出它會如何影響性能。 – kha

回答

1

你可以試試這個代碼:

bool Stop = false; 
    Thread thread; 

    private void StartButton_Click(object sender, EventArgs e) 
    { 
     string FileName = @"...\a.bin"; 
     thread = new Thread(new ThreadStart(() => DoLongProcess(FileName))); 
     thread.IsBackground = true; 
     thread.Start(); 
    } 

    private void StopButton_Click(object sender, EventArgs e) 
    { 
     Stop = true; 
    } 


    private void DoLongProcess(string file) 
    { 
     using (BinaryReader reader = new BinaryReader(File.Open(file, FileMode.Open))) 
     { 
      int pos = 0; 
      int length = (int)reader.BaseStream.Length; 
      while (pos < length) 
      { 
       if (Stop) 
        thread.Abort(); 
       // using Invoke if you want cross UI objects 
       this.Invoke((MethodInvoker)delegate 
       { 
        label1.Text = pos.ToString(); 
       }); 
       pos += sizeof(int); 
      } 
     } 
    } 
+0

嗨,bebosh。我已經添加了一個TestButton並使用了你的代碼,但是如何添加你的示例的結構來使用字符串參數運行我的函數,並且能夠使用stop按鈕在任何時候取消? – Sarmeu

+0

謝謝bebosh,請在原始帖子中查看我的update3。我仍然懷疑如何在你的功能中應用你的例子。 – Sarmeu

+0

嗨bebosh,問題是函數需要一個字符串參數被調用。被定義爲「private void DoLongProcess(string file){..}」。然後在StartButton_Click()應該被調用類似這樣的「thread = new Thread(new ThreadStart(DoLongProcess(filename)));」但是,當我在代碼中執行此操作時收到錯誤。 – Sarmeu

-1

使用中斷線程

 Thread thread; 

     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     private async void StartButtonClick(object sender, RoutedEventArgs e) 
     { 
      thread = new Thread(ExecuteLong); 
      var task = Task.Run(() => 
       thread.Start()); 
      await task; 
     } 

     private void ExecuteLong() 
     { 
      try 
      { 
       // long task 
      } 
      catch (ThreadInterruptedException e) 
      { 
       MessageBox.Show("cancelled!"); 
       return; 
      } 
      MessageBox.Show("finished"); 
     } 

     private void CancelButtonClick(object sender, RoutedEventArgs e) 
     { 
      this.thread.Interrupt(); 
     } 
+2

'var task = Task.Run(()=> thread.Start());等待任務;'這段代碼沒有任何意義。 – svick

2

貝博什的回答非常好。爲了進一步提高性能,可以在設置「thread.IsBackground = true;」之後立即設置「.Priority = ThreadPriority.AboveNormal」來設置「線程」的ThreadPriority。