2016-08-15 60 views
1

我的應用程序有點問題。 我有一個數據庫編輯器,當我嘗試更新數據庫文件時有時會掛起。並非每次,而是經常發生,並且每次在數據庫發生任何更改之前都會發生。我想這是因爲沒有使用多線程。儘管我最近纔開始學習編程,但即使閱讀了幾條多線程解釋之後,我仍然迷失了方向。有人可以向我解釋我應該如何在我的具體示例中實現它?什麼是多線程的最佳方式?

 
    private void adjustStatsButton_Click(object sender, EventArgs e) 
    { 
      ReadWrite.AdjustStats(winnerInput.Text, loserInput.Text); 
      winnerInput.Text = ""; 
      loserInput.Text = ""; 
      Refresh(leaderboardBox); 
    } 

 
    public class ReadWrite 
    { 
     public static void AdjustStats(string winner, string loser) 
     { 
      SQLiteConnection dbConnection = new SQLiteConnection("Data Source = Leaderboards.sqlite; Version = 3");

string sql = "SELECT * FROM leaderboard WHERE name='" + winner + "'"; SQLiteCommand command = new SQLiteCommand(sql, dbConnection); dbConnection.Open(); SQLiteDataReader reader = command.ExecuteReader(); double wrating = Convert.ToDouble(reader["rating"]); int wmatches = Convert.ToInt32(reader["matches"]); int wwins = Convert.ToInt32(reader["wins"]); sql = "SELECT * FROM leaderboard WHERE name='" + loser + "'"; command = new SQLiteCommand(sql, dbConnection); reader = command.ExecuteReader(); double lrating = Convert.ToDouble(reader["rating"]); int lmatches = Convert.ToInt32(reader["matches"]); int lwins = Convert.ToInt32(reader["wins"]); int llosses = Convert.ToInt32(reader["losses"]); double RC = (1 - ((wrating - lrating)/200)) * 8; if (RC < 0) RC *= -1; if (RC < 4) RC = 4; else if (RC > 12) RC = 12; wmatches++; wwins++; lmatches++; llosses++; wrating += RC; if (wrating < 0) wrating = 0; lrating -= RC; if (lrating < 0) lrating = 0; double wwinrate = Convert.ToDouble(wwins)/wmatches; double lwinrate = Convert.ToDouble(lwins)/lmatches; sql = "UPDATE leaderboard SET rating=" + wrating + ", matches=" + wmatches + ", wins=" + wwins + ", winrate=" + wwinrate + " WHERE name='" + winner + "'"; command = new SQLiteCommand(sql, dbConnection); command.ExecuteNonQuery(); sql = "UPDATE leaderboard SET rating=" + lrating + ", matches=" + lmatches + ", losses=" + llosses + ", winrate=" + lwinrate + " WHERE name='" + loser + "'"; command = new SQLiteCommand(sql, dbConnection); command.ExecuteNonQuery(); dbConnection.Close(); } }
+0

什麼'刷新(leaderboardBox );'do? –

+0

注意,注意參數名稱爲'winner'和'loser'的sql注入 – smoksnes

+2

首先,你提供的代碼看起來並不是超級笨重,但是看起來像你這樣做在GUI線程中,你應該避免這種情況。嘗試使用'BackgroundWorker',如這裏所述http://stackoverflow.com/questions/6365887/can-you-link-to-a-good-example-of-using- backgroundworker-without-place-it-on-a – smoksnes

回答

0

問題是你正在UI線程中運行查詢。下面是使用BackgroundWorker,沉重啓發從this example,並使用Arguments的示例。這樣它將在單獨的線程中運行,並且不會鎖定GUI。

// Class for passing arguments 
public class BqArguments 
{ 
    public string Winner {get;set} 
    public string Loser {get;set} 
} 

以及實現:

BackgroundWorker _bw; // You need to initialize this somewhere. 

private void adjustStatsButton_Click(object sender, EventArgs e) 
{ 
    // Maybe this should be initialized in ctor. But for this example we do it here... 
    _bw = new BackgroundWorker(); 
    var arguments = new BqArguments 
    { 
     Winner = winnerInput.Text, 
     Loser = loserInput.Text 
    } 
    _bw.DoWork += bw_DoWork; 
    _bw.RunWorkerCompleted += bw_RunWorkerCompleted; 
    _bw.RunWorkerAsync(arguments);  
} 

private void bw_DoWork (object sender, DoWorkEventArgs e) 
{ 
    // Run your query in the background. 
    var arguments = e.Argument as BqArguments; 
    ReadWrite.AdjustStats(arguments.Winner, arguments.Loser); 
} 

private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    // All done. Let's update the GUI. 
    winnerInput.Text = ""; 
    loserInput.Text = ""; 
    Refresh(leaderboardBox); 
} 
+0

工程很好,但我不得不把'b.w'中的'e.Arguments'改成'e.Argument',因爲顯然是'' DoWorkEventArgs'沒有包含'Arguments'的定義「 – Draxuss

+0

是的,我花了一點時間試圖弄清楚它爲什麼不能正常工作,但現在都很好。謝謝! – Draxuss

0

你只需要使用異步C#代碼中等待的關鍵字,並最終會導致DB我已經修改了你的代碼異步調用:

private async void adjustStatsButton_Click(object sender, EventArgs e) 
    { 
      await ReadWrite.AdjustStats(winnerInput.Text, loserInput.Text); 
      winnerInput.Text = ""; 
      loserInput.Text = ""; 
      Refresh(leaderboardBox); 
    } 


    public class ReadWrite 
     { 
      public static Task AdjustStats(string winner, string loser) 
      { 
    return Task.Run(() => 
    { 
     SQLiteConnection dbConnection = new SQLiteConnection("Data Source = Leaderboards.sqlite; Version = 3"); 


      string sql = "SELECT * FROM leaderboard WHERE name='" + winner + "'"; 
      SQLiteCommand command = new SQLiteCommand(sql, dbConnection); 
      dbConnection.Open(); 
      SQLiteDataReader reader = await command.ExecuteReaderAsync(); 


      double wrating = Convert.ToDouble(reader["rating"]); 
      int wmatches = Convert.ToInt32(reader["matches"]); 
      int wwins = Convert.ToInt32(reader["wins"]); 

      sql = "SELECT * FROM leaderboard WHERE name='" + loser + "'"; 
      command = new SQLiteCommand(sql, dbConnection); 
      reader = await command.ExecuteReaderAsync(); 

      double lrating = Convert.ToDouble(reader["rating"]); 
      int lmatches = Convert.ToInt32(reader["matches"]); 
      int lwins = Convert.ToInt32(reader["wins"]); 
      int llosses = Convert.ToInt32(reader["losses"]); 

      double RC = (1 - ((wrating - lrating)/200)) * 8; 
      if (RC < 0) RC *= -1; 
      if (RC < 4) RC = 4; 
      else if (RC > 12) RC = 12; 

      wmatches++; 
      wwins++; 
      lmatches++; 
      llosses++; 
      wrating += RC; 
      if (wrating < 0) wrating = 0; 
      lrating -= RC; 
      if (lrating < 0) lrating = 0; 
      double wwinrate = Convert.ToDouble(wwins)/wmatches; 
      double lwinrate = Convert.ToDouble(lwins)/lmatches; 

      sql = "UPDATE leaderboard SET rating=" + wrating + ", matches=" + wmatches + ", wins=" + wwins + ", winrate=" + wwinrate + " WHERE name='" + winner + "'"; 
      command = new SQLiteCommand(sql, dbConnection); 
      await command.ExecuteNonQueryAsync(); 

      sql = "UPDATE leaderboard SET rating=" + lrating + ", matches=" + lmatches + ", losses=" + llosses + ", winrate=" + lwinrate + " WHERE name='" + loser + "'"; 
      command = new SQLiteCommand(sql, dbConnection); 
      await command.ExecuteNonQueryAsync(); 
      dbConnection.Close(); 
      } 
}); 

     } 
+0

這是您使用的單個線程/任務。你可以做的勝利和失敗的線程與等待的第三線程,其餘的代碼,即從'雙wrating = Convert.ToDouble'的第三線程 – Neil

+0

我沒有使用這種方法,因爲一個線程就足夠了爲了讓你的UI響應用戶 – Lakhtey

+0

@Draxuss這是Lakhtey的答案不是我的。 :) – Neil

0

你的問題的核心是,我們在調用阻塞UI線程(同步)數據庫調用。這會在您等待數據庫時阻塞您的UI線程,而不是保持良好的響應。

在一般情況下,由於這些是基於I/O的操作,因此應該能夠使它們自然異步,按照Lakhtey's answer但是,SQLite不支持實際的異步操作。 :(

因此,在這種情況下,最好的辦法是剛剛收官數據庫工作到後臺線程注意使用Task.Run遠遠優於BackgroundWorker

private async void adjustStatsButton_Click(object sender, EventArgs e) 
{ 
    await Task.Run(() => ReadWrite.AdjustStats(winnerInput.Text, loserInput.Text)); 
    winnerInput.Text = ""; 
    loserInput.Text = ""; 
    Refresh(leaderboardBox); 
} 
+0

謝謝Stephen。什麼是最好的選擇這裏當你說優越嗎? – Zuzlx

+0

@Zuzlx:對於SQLite,我會用'Task.Run'。 –

+0

@StephenCleary所以我需要做的唯一事情就是添加'await Task.Run((=)[]],它會自動在不同的線程上處理它。它不會導致'winnerInput.Text'被設置爲'null'的問題? – Draxuss

相關問題