使用任务的多线程.工厂和实体框架
本文关键字:实体 框架 工厂 多线程 任务 | 更新日期: 2023-09-27 17:50:13
我有一个应用程序,它将系统更新通知SMS数据库中的每个订阅者。它使用实体框架选择每条记录,然后创建一个新的Task向该人员发送消息。理论上,这应该是一个快速的过程。我做错了什么,因为它每一两秒只能得到一个完整的响应。
我认为这个问题与我在Task.Factory.StartNew()
中设置任务的方式有关。它看起来像在同步运行,但我想让它异步运行。
下面是我的代码:
class Program
{
static List<MessageToSend> Messages = new List<MessageToSend>();
static Entities oDatabase = new Entities();
static SMS.API oAPI = new SMS.API();
const string sAuthToken = "*****";
const string sNotificationMessage = "*****";
static void Main(string[] args)
{
foreach (var subscriber in oDatabase.SMS_Subscribers.Where(x => x.GlobalOptOut == false))
{
MessageToSend oMessage = new MessageToSend();
oMessage.ID = subscriber.ID;
oMessage.MobileNumber = subscriber.MobileNumber;
var recentlySentMessage = oDatabase.SMS_OutgoingMessages.Where(x => x.Message == sNotificationMessage && x.MobileNumber == oMessage.MobileNumber && x.Sent > new DateTime(2014, 3, 12)).FirstOrDefault();
if (recentlySentMessage != null)
{
oMessage.Completed = true;
continue;
}
Task t = Task.Factory.StartNew(() =>
{
try{
var keywordID = oDatabase.SMS_SubscribersKeywords.Where(x => x.SubscriberID == oMessage.ID).First().KeywordID;
var keyword = oDatabase.SMS_Keywords.Where(x => x.ID == keywordID).First();
oMessage.DemographicID = keyword.DemographicID;
oMessage.Keyword = keyword.Keyword;
SendNotificationMessage(oMessage);
}
catch (Exception oEx){ //Write exception to console}
});
Thread.Sleep(15);
}
while (Messages.ToList().Any(x => !x.Completed)){ //wait till all are completed}
}
public static void SendNotificationMessage(object message)
{
MessageToSend oMessage = (MessageToSend)message;
try
{
SMS.APIResponse oResponse = oAPI.SendMessage(sAuthToken, oMessage.DemographicID, oMessage.Keyword, oMessage.MobileNumber, sNotificationMessage);
if (oResponse.Success){ //Write success to console }
else{ //Write failure to console }
}
catch (Exception oEx){ //Write Exception to console }
oMessage.Completed = true;
}
}
class MessageToSend
{
public long ID { get; set; }
public long DemographicID {get;set;}
public string MobileNumber { get; set; }
public bool Completed { get; set; }
public string Keyword { get; set; }
public MessageToSend(){ Completed = false; }
}
编辑:foreach块的内部现在看起来像这样:
MessageToSend oMessage = new MessageToSend();
oMessage.ID = subscriber.ID;
oMessage.MobileNumber = subscriber.MobileNumber;
int keywordID = 0;
SMSShortcodeMover.SMS_Keywords keyword;
var recentlySentMessage = oDatabase.SMS_OutgoingMessages.Where(x => x.Message == sNotificationMessage && x.MobileNumber == oMessage.MobileNumber && x.Sent > new DateTime(2014, 3, 12)).FirstOrDefault();
if (recentlySentMessage != null)
{
oMessage.Completed = true;
continue;
}
try
{
keywordID = (int)oDatabase.SMS_SubscribersKeywords.Where(x => x.SubscriberID == oMessage.ID).First().KeywordID;
keyword = oDatabase.SMS_Keywords.Where(x => x.ID == keywordID).First();
} catch (Exception oEx){ //write exception to console, then continue; }
Task t = Task.Factory.StartNew(() =>
{
oMessage.DemographicID = keyword.DemographicID;
oMessage.Keyword = keyword.Keyword;
SendNotificationMessage(oMessage);
});
Thread.Sleep(15);
}
编辑2:我再次更新了我的代码,现在我在发送之前收集了所有的数据。它仍然挂在某个地方,但它现在在大约5秒内获得了所有52,000行数据。代码看起来像这样:
var query =
(from subscriber in oDatabase.SMS_Subscribers
where subscriber.GlobalOptOut == false
where !(from x in oDatabase.SMS_OutgoingMessages
where x.Message == sNotificationMessage
where x.MobileNumber == subscriber.MobileNumber
where x.Sent > new DateTime(2014, 3, 12)
select x).Any()
join sk in oDatabase.SMS_SubscribersKeywords
on subscriber.ID equals sk.SubscriberID
join k in oDatabase.SMS_Keywords on sk.KeywordID equals k.ID into ks
from k2 in ks.Take(1)
select new MessageToSend()
{
ID = subscriber.ID,
MobileNumber = subscriber.MobileNumber,
DemographicID = k2.DemographicID,
Keyword = k2.Keyword
}).ToList();
foreach( var q in query){
Task t = Task.Factory.StartNew(() => SendNotificationMessage(q));
Tasks.Add(t);
Thread.Sleep(80);
}
Task.WaitAll(Tasks.ToArray());
如果我是你,我会尝试一次执行所有数据库调用,然后再尝试发送你的消息。
试试这样做:
var query =
from subscriber in oDatabase.SMS_Subscribers
where subscriber.GlobalOptOut == false
where !(from x in oDatabase.SMS_OutgoingMessages
where x.Message == sNotificationMessage
where x.MobileNumber == subscriber.MobileNumber
where x.Sent > new DateTime(2014, 3, 12)
select x
).Any()
join sk in oDatabase.SMS_SubscribersKeywords
on subscriber.ID equals sk.SubscriberID
join k in oDatabase.SMS_Keywords on sk.KeywordID equals k.ID into ks
from k2 in ks.Take(1)
select new
{
ID = subscriber.ID,
MobileNumber = subscriber.MobileNumber,
DemographicID = k2.DemographicID,
Keyword = k2.Keyword
};
var tasks =
from x in query.ToArray()
let message = new MessageToSend()
{
ID = x.ID,
MobileNumber = x.MobileNumber,
DemographicID = x.DemographicID,
Keyword = x.Keyword
}
select Task.Factory.StartNew(() => SendNotificationMessage(message));
Task.WaitAll(tasks.ToArray());
我没有你的数据库,所以我不能测试这个,但是像这样的东西应该工作,如果这不是完全正确的
对于每个循环的每次迭代花费1-2秒我并不感到惊讶,因为您有3个同步执行的独立数据库调用。从全局来看,数据库调用是相当缓慢的。解决这个问题的一种方法是使用一个方法,该方法拥有foreach块中除了Task代码之外的所有内容,然后使用Task来调用它。只需要注意Task Method中没有任何东西会阻塞。
。
var tasks = new List<Task>();
foreach (var subscriber in oDatabase.SMS_Subscribers.Where(x => x.GlobalOptOut == false))
{
tasks.Add(Task.Factory.StartNew(() => SendNotificationTask(subscriber));
Thread.Sleep(15);
}
//Might want to to use Task.WhenAll instead of WaitAll. Just need to debug it and see what happens.
Task.WaitAll(tasks.ToArray());
public void SendNotificationTask(SomeType subscriber)
{
MessageToSend oMessage = new MessageToSend();
oMessage.ID = subscriber.ID;
oMessage.MobileNumber = subscriber.MobileNumber;
int keywordID = 0;
SMSShortcodeMover.SMS_Keywords keyword;
////Database call 1
var recentlySentMessage = oDatabase.SMS_OutgoingMessages.Where(x => x.Message == sNotificationMessage && x.MobileNumber == oMessage.MobileNumber && x.Sent > new DateTime(2014, 3, 12)).FirstOrDefault();
if (recentlySentMessage != null)
{
oMessage.Completed = true;
}
else
{
try
{
////Database call 2
keywordID = (int)oDatabase.SMS_SubscribersKeywords.Where(x => x.SubscriberID == oMessage.ID).First().KeywordID;
////Database call 3
keyword = oDatabase.SMS_Keywords.Where(x => x.ID == keywordID).First();
} catch (Exception oEx){ //write exception to console, then continue; }
oMessage.DemographicID = keyword.DemographicID;
oMessage.Keyword = keyword.Keyword;
SendNotificationMessage(oMessage);
}
}