1

并行性是否有助于提高锁定对象的性能,应该单线程运行,还是有其他技术?

我注意到在访问数据集并从多个线程添加行时会引发异常。因此,我创建了一个“线程安全”版本,通过在更新行之前锁定表来添加行。此实现有效,但对于许多事务来说似乎很慢。

public partial class HaMmeRffl
{
    public partial class PlayerStatsDataTable
    {
        public void AddPlayerStatsRow(int PlayerID, int Year, int StatEnum, int Value, DateTime Timestamp)
        {
            lock (TeamMemberData.Dataset.PlayerStats)
            {
                HaMmeRffl.PlayerStatsRow testrow = TeamMemberData.Dataset.PlayerStats.FindByPlayerIDYearStatEnum(PlayerID, Year, StatEnum);
                if (testrow == null)
                {
                    HaMmeRffl.PlayerStatsRow newRow = TeamMemberData.Dataset.PlayerStats.NewPlayerStatsRow();
                    newRow.PlayerID = PlayerID;
                    newRow.Year = Year;
                    newRow.StatEnum = StatEnum;
                    newRow.Value = Value;
                    newRow.Timestamp = Timestamp;
                    TeamMemberData.Dataset.PlayerStats.AddPlayerStatsRow(newRow);
                }
                else
                {
                    testrow.Value = Value;
                    testrow.Timestamp = Timestamp;
                }

            }
        }
    }
}

现在我可以从多个线程安全地调用它,但它真的给我买了什么吗?我可以以不同的方式执行此操作以获得更好的性能。例如,有没有办法使用System.Collections.Concurrent命名空间来优化性能或任何其他方法?

另外,我在整个数据集更新后更新底层数据库,这需要很长时间。这是否会被视为 I/O 操作并且值得通过在数据集中的每一行(或一些行数)更新后更新它来使用并行处理。

更新

我编写了一些代码来测试并发处理与顺序处理,这表明执行并发处理需要大约 30% 的时间,我应该在这里使用顺序处理。我认为这是因为数据库上的锁定导致开销ConcurrentQueue比并行处理的收益更高。这个结论是否正确,我可以做些什么来加快处理速度,还是我坚持Datatable“你必须同步任何写操作”。

这是我的测试代码,可能在科学上不正确。这是定时器和它们之间的调用。

            dbTimer.Restart();
            Queue<HaMmeRffl.PlayersRow.PlayerValue> addPlayerRow = InsertToPlayerQ(addUpdatePlayers);
            Queue<HaMmeRffl.PlayerStatsRow.PlayerStatValue> addPlayerStatRow                     = InsertToPlayerStatQ(addUpdatePlayers);
            UpdatePlayerStatsInDB(addPlayerRow, addPlayerStatRow);
            dbTimer.Stop();
            System.Diagnostics.Debug.Print("Writing to the dataset took {0} seconds single threaded", dbTimer.Elapsed.TotalSeconds);

            dbTimer.Restart();
            ConcurrentQueue<HaMmeRffl.PlayersRow.PlayerValue> addPlayerRows                    = InsertToPlayerQueue(addUpdatePlayers);
            ConcurrentQueue<HaMmeRffl.PlayerStatsRow.PlayerStatValue> addPlayerStatRows                    = InsertToPlayerStatQueue(addUpdatePlayers);
            UpdatePlayerStatsInDB(addPlayerRows, addPlayerStatRows);
            dbTimer.Stop();
            System.Diagnostics.Debug.Print("Writing to the dataset took {0} seconds concurrently", dbTimer.Elapsed.TotalSeconds);

在这两个示例中,我以相同的方式添加到QueueConcurrentQueue单线程。唯一的区别是插入到数据表中。单线程方法插入如下:

    private static void UpdatePlayerStatsInDB(Queue<HaMmeRffl.PlayersRow.PlayerValue> addPlayerRows, Queue<HaMmeRffl.PlayerStatsRow.PlayerStatValue> addPlayerStatRows)
    {
        try
        {
            HaMmeRffl.PlayersRow.PlayerValue row;
            while (addPlayerRows.Count > 0)
            {
                row = addPlayerRows.Dequeue();
                TeamMemberData.Dataset.Players.AddPlayersRow(
                    row.PlayerID, row.Name, row.PosEnum, row.DepthEnum,
                    row.TeamID, row.RosterTimestamp, row.DepthTimestamp,
                    row.Active, row.NewsUpdate);
            }
        }
        catch (Exception)
        {
            TeamMemberData.Dataset.Players.RejectChanges();
        }

        try
        {
            HaMmeRffl.PlayerStatsRow.PlayerStatValue row;
            while (addPlayerStatRows.Count > 0)
            {
                row = addPlayerStatRows.Dequeue();
                TeamMemberData.Dataset.PlayerStats.AddUpdatePlayerStatsRow(
                    row.PlayerID, row.Year, row.StatEnum, row.Value, row.Timestamp);
            }
        }
        catch (Exception)
        {
            TeamMemberData.Dataset.PlayerStats.RejectChanges();
        }

        TeamMemberData.Dataset.Players.AcceptChanges();
        TeamMemberData.Dataset.PlayerStats.AcceptChanges();

    }

并发添加如下

    private static void UpdatePlayerStatsInDB(ConcurrentQueue<HaMmeRffl.PlayersRow.PlayerValue> addPlayerRows, ConcurrentQueue<HaMmeRffl.PlayerStatsRow.PlayerStatValue> addPlayerStatRows)
    {
        Action actionPlayer = () =>
        {
            HaMmeRffl.PlayersRow.PlayerValue row;
            while (addPlayerRows.TryDequeue(out row))
            {
                TeamMemberData.Dataset.Players.AddPlayersRow(
                    row.PlayerID, row.Name, row.PosEnum, row.DepthEnum,
                    row.TeamID, row.RosterTimestamp, row.DepthTimestamp,
                    row.Active, row.NewsUpdate);
            }
        };

        Action actionPlayerStat = () =>
        {
            HaMmeRffl.PlayerStatsRow.PlayerStatValue row;
            while (addPlayerStatRows.TryDequeue(out row))
            {
                TeamMemberData.Dataset.PlayerStats.AddUpdatePlayerStatsRow(
                    row.PlayerID, row.Year, row.StatEnum, row.Value, row.Timestamp);
            }
        };

        Action[] actions = new Action[Environment.ProcessorCount * 2];
        for (int i = 0; i < Environment.ProcessorCount; i++)
        {
            actions[i * 2] = actionPlayer;
            actions[i * 2 + 1] = actionPlayerStat;
        }

        try
        {
            // Start ProcessorCount concurrent consuming actions.
            Parallel.Invoke(actions);
        }
        catch (Exception)
        {
            TeamMemberData.Dataset.Players.RejectChanges();
            TeamMemberData.Dataset.PlayerStats.RejectChanges();
        }

        TeamMemberData.Dataset.Players.AcceptChanges();
        TeamMemberData.Dataset.PlayerStats.AcceptChanges();

    }

单线程的时间差为 4.6 秒,并行调用的时间差为 6.1 秒。

4

2 回答 2

1

锁定和事务不利于并行性和性能。

1)尽量避免锁:不同线程是否需要更新数据集中的同一行?

2)最小化锁定时间。

对于数据库操作使用可以尝试 ADO.NET 的批量更新未来:http: //msdn.microsoft.com/en-us/library/ms810297.aspx

于 2013-09-27T03:44:52.363 回答
0

多线程可以在一定程度上提供帮助,因为一旦数据越过应用程序边界,您将开始等待 I/O,在这里您可以进行异步处理,因为您的应用程序无法控制各种参数(资源访问、网络速度等),这将提供更好的用户体验(如果是 UI 应用程序)。

现在对于您的场景,您可能希望使用某种生产者/消费者队列,只要队列中有一行可用,另一个线程就会开始处理它,但这会在一定程度上起作用。

于 2013-10-01T04:16:14.353 回答