简体   繁体   English

什么是多线程的最佳方式?

[英]What's the best way to multithread?

I've got a little problem with my application. 我的申请有点问题。 I have a database editor that hangs up sometimes when I try to update the database file. 我有一个数据库编辑器,当我尝试更新数据库文件时,它有时会挂起。 Not every time, but pretty often, and every time it happens right before any changes are made to the database. 并非每次,但经常,并且每次在数据库发生任何更改之前发生。 I figured it's because of not using multithreading. 我认为这是因为没有使用多线程。 I've only started learning programming recently though, and I'm kind of lost even after reading through a few multithreading explanations. 我最近才开始学习编程,即使阅读了一些多线程解释,我也有点迷失。 Could someone explain to me how should I implement it in my specific example? 有人可以向我解释我应该如何在我的具体例子中实现它?

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(); } }

You just need to use async await keywords within your c# code and it will eventually lead Asynchronous calls to the DB I have made changes to your Code : 您只需要在c#代码中使用async await关键字,它最终将导致异步调用数据库我已对代码进行了更改:

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();
            }
});

        }

The problem is that you are running the query in the UI thread. 问题是您在UI线程中运行查询。 Here's an example for using the BackgroundWorker , heavily inspired from this example , and using Arguments . 下面是使用一个例子BackgroundWorker ,从很大程度上激发了这个例子 ,并使用Arguments This way it will be running in a separate thread and it will not lock the GUI. 这样它将在一个单独的线程中运行,它不会锁定GUI。

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

And your implementation: 你的实施:

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);
}

The core of your problem is that you're calling blocking (synchronous) database calls on the UI thread. 您的问题的核心是您在UI线程上调用阻塞(同步)数据库调用。 This blocks your UI thread while it's waiting for the database, instead of keeping it nicely responsive. 这会阻止您的UI线程在等待数据库时,而不是保持响应良好。

In the general case, since these are I/O-based operations, you should be able to make them naturally asynchronous, as per Lakhtey's answer . 在一般情况下,由于这些是基于I / O的操作, 因此根据Lakhtey的回答 ,您应该能够使它们自然地异步。 However , SQLite does not support actual asynchronous operations. 但是 ,SQLite不支持实际的异步操作。 :( :(

So, in this case, your best bet is to just wrap up the database work into a background thread. 因此,在这种情况下,最好的办法是将数据库工作包装到后台线程中。 Note that using Task.Run is far superior to BackgroundWorker : 请注意,使用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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM