2

アプリケーション システムが 1 分ごとにオンライン ユーザーにメッセージを発行する必要があり、コードがマルチスレッド タスクを使用してメッセージ リストを読み取る場合があります。しかし、プログラムは正しく実行されず、範囲外のインデックス例外がスローされます。誰か提案してください、ありがとう。

private Timer taskTimer;
private static readonly object _locker = new object();
private static IList<Message> _messages = null;

private void OnTimerElapsed(object sender)
{
    var msgModel = new MessageModel();
    _messages = msgModel.GetMessageList();
    var msgCount = _messages.Count();

    Task[] _tasks = new Task[msgCount];
    for (int i = 0; i < msgCount; i++)
    {
        if (i < msgCount)
        {
            _tasks[i] = Task.Factory.StartNew(() =>
            {
                lock (_locker)
                {
                    PushMessage(i);
                }
            });
        }
    }

    //waiting all task finished
    while (_tasks.Any(t => !t.IsCompleted)) { }
}

private void PushMessage(int i)
{          
    var msg = _messages[i];         //it will throw an exception here...
    //send message to on line users.
    SendToOnlineUsers(msg);
}

Error:
Index was out of range. Must be non-negative and less than the size of the collection.

StackTrace Details:
   at System.ThrowHelper.ThrowArgumentOutOfRangeException()
   at System.Collections.Generic.List`1.get_Item(Int32 index)
   at WebIM.Hubs.BackgroudPushServiceTimer.PushMessage(Int32 i) in ...
   at WebIM.Hubs.BackgroudPushServiceTimer.<>c__DisplayClass6.<OnTimerElapsed>b__2() in ...
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()

メッセージ数が 4 で、PushMessage 関数でインデックスも 4 になる場合は、範囲外です。

4

1 に答える 1

5

i問題は、値が時間とともに変化する変数である をキャプチャしていることです。ループ内でローカル コピーを作成することで修正できます。

for (int i = 0; i < msgCount; i++)
{
    int copyOfI = i;
    if (i < msgCount)
    {
        _tasks[i] = Task.Factory.StartNew(() =>
        {
            lock (_locker)
            {
                PushMessage(copyOfI);
            }
        });
    }
}

そうは言っても、これを処理するよりクリーンな方法があると思います-特に、すべて同じロックを使用しているいくつかのタスクを作成しているためです。ここでは実際には並行性を達成しておらず、すべてが完了するのを待っています。

また、Task.WhenAll/Task.WaitAllは、タスクの完了を待機するより効率的な方法と見なす必要があります。

編集:これを並行して実行できる場合は、代わりにParallel.Fororを使用することを検討してください。Parallel.ForEach

于 2013-06-26T09:38:52.603 に答える