C# 使用任务工厂和实体框架的多线程?

C# 使用任务工厂和实体框架的多线程?,c#,entity-framework,asynchronous,task,C#,Entity Framework,Asynchronous,Task,我有一个应用程序,通知我们SMS数据库中的每个订户系统的更新。它使用实体框架来选择每个记录,然后创建一个新任务来向此人发送消息。理论上,这应该是一个快速的过程。我做错了,因为它每一两秒钟只得到一个完整的响应 我认为问题与我在Task.Factory.StartNew()中设置任务的方式有关。它的行为就像是同步运行,但我希望它异步运行 如果我对如何使用任务完全不了解,请告诉我。我从中得到灵感 这是我的密码: class Program { static List<MessageToSend&

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

我认为问题与我在
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; }
}
编辑2: 我再次更新了代码,现在我在进入发送之前收集了所有数据。它仍然挂在某个地方,但现在它在大约5秒钟内获得了所有52000行数据。代码如下所示:

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

对于for-each循环的每次迭代需要1-2秒,这并不奇怪,因为有3个单独的数据库调用是同步执行的。从总体上看,数据库调用相当缓慢。一种方法是使用一个方法,该方法在foreach块中包含除任务代码之外的所有内容,然后使用Task调用它。只需要小心,任务方法中没有任何东西会阻塞

var tasks=newlist();
foreach(oDatabase.SMS_Subscribers.Where(x=>x.GlobalOptOut==false)中的var subscriber)
{
tasks.Add(Task.Factory.StartNew(()=>SendNotificationTask(订阅服务器));
睡眠(15);
}
//可能需要使用Task.WhenAll而不是WaitAll。只需调试它,看看会发生什么。
Task.WaitAll(tasks.ToArray());
public void SendNotificationTask(SomeType订阅服务器)
{
MessageToSend omemessage=newmessagetosend();
oMessage.ID=subscriber.ID;
oMessage.MobileNumber=订户.MobileNumber;
int关键字id=0;
SMSShortcodeMover.SMS_关键字;
////数据库调用1
var recentlySentMessage=oDatabase.SMS_OutgoingMessages.Where(x=>x.Message==sNotificationMessage&&x.MobileNumber==omemessage.MobileNumber&&x.Sent>new DateTime(2014,3,12)).FirstOrDefault();
if(recentlySentMessage!=null)
{
oMessage.Completed=true;
}
其他的
{
尝试
{
////数据库调用2
keywordID=(int)oDatabase.SMS_subscriberKeywords.Where(x=>x.SubscriberID==oMessage.ID).First().keywordID;
////数据库呼叫3
keyword=oDatabase.SMS_Keywords.Where(x=>x.ID==keywordID.First();
}catch(异常oEx){//将异常写入控制台,然后继续;}
oMessage.DemographicID=关键字.DemographicID;
oMessage.Keyword=关键字.Keyword;
发送通知消息(oMessage);
}

}

如果我是你,我会在尝试接收你的消息之前,尝试一次执行所有数据库调用

尝试这样做:

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

我没有你的数据库,所以我无法测试它,但是如果这不完全正确,类似的东西应该可以工作。

首先,尝试在任务之外执行
oDatabase
查询。还有,为什么你的代码有
线程。Sleep(15)
?我有
线程。Sleep(15)
来限制循环的速度。我们可以为我们的短信提供商生成多少流量是有限制的。我最初在任务之外有
oDatabase
查询,但我把它们放在任务里面,试图阻止它们阻塞剩下的代码。不过,这似乎没有帮助。有一些不好的地方在这里,这就是为什么我有所有的try/catch块。这个查询会避免这些吗?还有,非常感谢。我从来没有想过这样做。这是一个伟大的方向,我仍在努力解决。我在这篇文章中遇到了一个类似于此的错误,我正在试图找出如何调整最后的选择:@ijb109-我已经修改了优化查询以消除您提到的错误。基本上,我将数据库查询与所需对象的构建分离开来。请注意
query.ToArray()
强制执行数据库查询的调用。现在应该更接近工作状态了。以更高效的方式获取数据后,问题似乎出在任务的某个地方。感觉好像只使用了两个或三个线程,或者所有线程都挂在web请求上。感谢您帮助获取dat一个已解决的问题!@ijb109-可能是您的
SMS.API
可能不是线程安全的。而且TPL将自身限制为几个线程。
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());