使用Task.Factory和Entity Framework进行多线程处理?

时间:2014-03-13 20:21:15

标签: c# entity-framework asynchronous task

我有一个应用程序通知我们的SMS数据库中的每个订户我们的系统更新。它使用实体框架选择每个记录,然后创建一个新任务以向该人发送消息。从理论上讲,这应该是一个快速的过程。我做错了什么,因为它每两秒或两秒才会得到一个完整的回复。

我认为问题与我在Task.Factory.StartNew()中设置任务的方式有关。它表现得像是在同步运行,但我想让它以异步方式运行。

如果我完全偏离了我使用任务的方式,请告诉我。我从this post得到了灵感。

这是我的代码:

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());

2 个答案:

答案 0 :(得分:1)

如果我是你,我会尝试在尝试发送你的消息之前立即执行所有数据库调用。

尝试这样做:

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 :(得分:0)

对于每个循环的每次迭代,它都需要1-2秒才会让我感到惊讶,因为您有3个独立的数据库调用同步执行。在宏观方案中,数据库调用相当缓慢。解决这个问题的一种方法是让一个方法在foreach块中包含除Task任务代码之外的所有内容,然后使用task来调用它。只需要注意,任务方法中的任何内容都不会阻塞。

即。

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);

   }

}