У меня есть приложение для телефонных сообщений, в котором нужно обработать очень много сообщений, поскольку телефонные порты ограничены, поэтому сообщение будет обработано в первую очередь. У каждого сообщения есть флаг «Подтверждение», который указывает, какое сообщение обрабатывается. Конечно, он был инициализирован как ложный.
Я хочу поместить все сообщения в очередь, а затем обработать их несколькими потоками или задачами.
public class MessageQueue
{
public Queue MessageWorkItem { get; set; }
public Messages Message { get; set; }
public MessageQueue()
{
MessageWorkItem = new Queue();
Message = new Messages();
}
public void GetMessageMetaData()
{
try
{
// It is just a test, add only one item into the queue
Message.MessageID = Guid.NewGuid();
Message.NumberToCall = "1111111111";
Message.FacilityID = "3333";
Message.NumberToDial = "2222222222";
Message.CountryCode = "1";
Message.Acknowledge = false;
}
catch (Exception ex)
{
}
}
public void AddingItemToQueue()
{
GetMessageMetaData();
if (!Message.Acknowledge)
{
lock (MessageWorkItem)
{
MessageWorkItem.Enqueue(Message);
}
}
}
}
public class Messages
{
public Guid MessageID { get; set; }
public string NumberToCall { get; set; }
public string FacilityID { get; set; }
public string NumberToDial { get; set; }
public string CountryCode { get; set; }
public bool Acknowledge { get; set; }
}
Теперь у меня вопрос, как удалить элемент из очереди с помощью многопоточности. Для каждого элемента из очереди я хочу запустить сценарий.
public void RunScript(Message item)
{
try
{
PlayMessage(item);
return;
}
catch (HangupException hex)
{
Log.WriteWithId("Caller Hungup!", hex.Message);
}
catch (Exception ex)
{
Log.WriteException(ex, "Unexpected exception: {0}");
}
}
Я думал, что посмотреть, если
if (MessageWorkItem.Count> = 1) Затем что-то делаю, но мне нужна помощь по коду.
2 ответа
Если вы можете использовать .Net 4.5, я бы посоветовал взглянуть на поток данных из задачи Параллельная библиотека (TPL).
Эта страница приводит к множеству примеров пошаговых руководств, таких как Как: реализовать шаблон потока данных производитель-потребитель и Пошаговое руководство: использование потока данных в приложении Windows Forms.
Взгляните на эту документацию, чтобы узнать, поможет ли она вам. Это довольно много, но я думаю, что это, вероятно, ваш лучший подход.
В качестве альтернативы вы можете изучить использование {{X0 }} вместе с его методом GetConsumingEnumerable()
для доступа к элементам в очереди.
Что вы делаете, так это разбиваете работу на объекты, которые хотите каким-то образом обработать, и используете BlockingCollection для управления очередью.
Пример кода, использующего ints
вместо объектов в качестве рабочих элементов, поможет продемонстрировать это:
Когда рабочий поток завершит работу со своим текущим элементом, он удалит новый элемент из рабочей очереди, обработает этот элемент, а затем добавит его в очередь вывода.
Отдельный потребительский поток удаляет завершенные элементы из очереди вывода и что-то с ними делает.
В конце мы должны дождаться завершения всех рабочих процессов (Task.WaitAll (worker)), прежде чем мы сможем отметить очередь вывода как завершенную (outputQueue.CompleteAdding ()).
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
new Program().run();
}
void run()
{
int threadCount = 4;
Task[] workers = new Task[threadCount];
Task.Factory.StartNew(consumer);
for (int i = 0; i < threadCount; ++i)
{
int workerId = i;
Task task = new Task(() => worker(workerId));
workers[i] = task;
task.Start();
}
for (int i = 0; i < 100; ++i)
{
Console.WriteLine("Queueing work item {0}", i);
inputQueue.Add(i);
Thread.Sleep(50);
}
Console.WriteLine("Stopping adding.");
inputQueue.CompleteAdding();
Task.WaitAll(workers);
outputQueue.CompleteAdding();
Console.WriteLine("Done.");
Console.ReadLine();
}
void worker(int workerId)
{
Console.WriteLine("Worker {0} is starting.", workerId);
foreach (var workItem in inputQueue.GetConsumingEnumerable())
{
Console.WriteLine("Worker {0} is processing item {1}", workerId, workItem);
Thread.Sleep(100); // Simulate work.
outputQueue.Add(workItem); // Output completed item.
}
Console.WriteLine("Worker {0} is stopping.", workerId);
}
void consumer()
{
Console.WriteLine("Consumer is starting.");
foreach (var workItem in outputQueue.GetConsumingEnumerable())
{
Console.WriteLine("Consumer is using item {0}", workItem);
Thread.Sleep(25);
}
Console.WriteLine("Consumer is finished.");
}
BlockingCollection<int> inputQueue = new BlockingCollection<int>();
BlockingCollection<int> outputQueue = new BlockingCollection<int>();
}
}
workItem
, который вы создадите любого типа, который вам нужен для хранения информации, которую вы хотите обрабатывать как единицу работы - возможно, экземпляр Message
в вашем случае? В этом случае вы должны использовать BlockingCollection<Message>
Parallel.ForEach из TPL. Это параллельно для каждого.
Пример (изменен MessageWorkItem на общую очередь):
public class MessageQueue
{
public Queue<Message> MessageWorkItem { get; set; }
public MessageQueue()
{
MessageWorkItem = new Queue<Message>();
}
public Message GetMessageMetaData()
{
try
{
// It is just a test, add only one item into the queue
return new Message()
{
MessageID = Guid.NewGuid(),
NumberToCall = "1111111111",
FacilityID = "3333",
NumberToDial = "2222222222",
CountryCode = "1",
Acknowledge = false
};
}
catch (Exception ex)
{
return null;
}
}
public void AddingItemToQueue()
{
var message = GetMessageMetaData();
if (!message.Acknowledge)
{
lock (MessageWorkItem)
{
MessageWorkItem.Enqueue(message);
}
}
}
}
public class Message
{
public Guid MessageID { get; set; }
public string NumberToCall { get; set; }
public string FacilityID { get; set; }
public string NumberToDial { get; set; }
public string CountryCode { get; set; }
public bool Acknowledge { get; set; }
}
class Program
{
static void Main(string[] args)
{
MessageQueue me = new MessageQueue();
for (int i = 0; i < 10000; i++)
me.AddingItemToQueue();
Console.WriteLine(me.MessageWorkItem.Count);
Parallel.ForEach(me.MessageWorkItem, RunScript);
}
static void RunScript(Message item)
{
// todo: ...
Console.WriteLine(item.MessageID);
Thread.Sleep(300);
}
}
Queue
просто не создан для одновременного доступа из нескольких потоков.
Parallel.ForEach
не будет выполнять многопоточный доступ к IEnumerable<T>
с использованием модуля разделения по умолчанию, он будет извлекать кучу элементов во внутреннюю очередь, используя один поток, а затем передавать эту работу нескольким рабочим потокам. Но снова я подчеркиваю, что Servy прав, что чтение из этого единственного потока и запись с использованием одного или нескольких других потоков невозможно с перечислителем Queue.
Похожие вопросы
Связанные вопросы
Новые вопросы
c#
C# (произносится как «see Sharp») — это высокоуровневый мультипарадигменный язык программирования со статической типизацией, разработанный Microsoft. Код C# обычно нацелен на семейство инструментов и сред выполнения Microsoft .NET, которое включает в себя .NET, .NET Framework, .NET MAUI и Xamarin среди прочих. Используйте этот тег для ответов на вопросы о коде, написанном на C#, или о формальной спецификации C#.