我正在尝试构建一个队列,以便在API给出生命迹象后将数据发送到API。
以下代码中的System.InvalidOperationException:
private void sendHandler()
{
while (true)
{
if (!sendQueueActive && sendQueue.Count >= 1)
{
sendQueueActive = true;
foreach (relays relays in sendQueue)
{
dynamic result = IoLogikApiConnector.put("io/relay", relays);
int code = result.error.code;
if (code != 0)
{
_log.logErrorToApi("Cannot write to IoLogik", "Error code:" + result, _deviceID);
_device.logErrorToApi();
sendQueue.Remove(relays);
}
else
{
_device.logConnectedToApi();
sendQueue.Remove(relays);
}
sendQueueActive = false;
}
}
else
{
Thread.Sleep(20);
}
}
}
答案 0 :(得分:2)
使用foreach时,您正在从队列中删除项目。绝不是一件好事。
最好写
using System.Linq;
using System.Collections.Generic;
using System.Collections;
private void sendHandler()
{
while (true)
{
if (!sendQueueActive && sendQueue.Count >= 1)
{
sendQueueActive = true;
// MAKE A COPY FIRST
var sendQueueCopy = sendQueue.ToList();
foreach (relays relays in sendQueueCopy)
{
dynamic result = IoLogikApiConnector.put("io/relay", relays);
int code = result.error.code;
if (code != 0)
{
_log.logErrorToApi("Cannot write to IoLogik", "Error code:" + result, _deviceID);
_device.logErrorToApi();
sendQueue.Remove(relays);
}
else
{
_device.logConnectedToApi();
sendQueue.Remove(relays);
}
sendQueueActive = false;
}
}
else
{
Thread.Sleep(20);
}
}
}
但更好的是使用线程安全队列。
https://msdn.microsoft.com/en-us/library/dd997371(v=vs.110).aspx
以下是上述链接中的剪切和粘贴示例
// A bounded collection. It can hold no more
// than 100 items at once.
BlockingCollection<Data> dataItems = new BlockingCollection<Data>(100);
// A simple blocking consumer with no cancellation.
Task.Run(() =>
{
while (!dataItems.IsCompleted)
{
Data data = null;
// Blocks if number.Count == 0
// IOE means that Take() was called on a completed collection.
// Some other thread can call CompleteAdding after we pass the
// IsCompleted check but before we call Take.
// In this example, we can simply catch the exception since the
// loop will break on the next iteration.
try
{
data = dataItems.Take();
}
catch (InvalidOperationException) { }
if (data != null)
{
Process(data);
}
}
Console.WriteLine("\r\nNo more items to take.");
});
// A simple blocking producer with no cancellation.
Task.Run(() =>
{
while (moreItemsToAdd)
{
Data data = GetData();
// Blocks if numbers.Count == dataItems.BoundedCapacity
dataItems.Add(data);
}
// Let consumer know we are done.
dataItems.CompleteAdding();
});