如何组织队列和睡眠的多线程

时间:2012-11-16 22:41:07

标签: c# winforms twitter

为什么我真的需要这个:(底部问题的简单描述,在edit3下)

让我们想象一下,我尝试为twitter创建应用程序。它拥有尽可能多的帐户,就像我现在或稍后添加(例如10)。因此,对于每个帐户,我们都有一个选项:

  1. 来自rss;
  2. 关注人;
  3. retwite any twites。
  4. 当我们谈论一个帐户时,每个任务都应该在后台工作并在其他任务中同步。对于其中一个帐户,一次性(请求太多)不会有任何进展和跟踪进行。

    哦,这意味着,我必须在一个时间内为每个帐户只做一个任务。这是否意味着,我必须为此时刻帐户中的每个活动组织一个帖子?但是什么是线程,告诉每个帐户下一步做什么(填充队列)?一个帐户想要叮叮当当,另一个帐户只是回归并跟随人们:

    1. (主题1)帐户A:希望整天都来自RSS /文件;
    2. (主题2)账户B:想每30秒重新开始一次,每15分钟跟随一次人;
    3. (主题3):应填写"帐户A" (制作twite,make twite,make twite);
    4. (主题4):应填写"帐户B" (再做一次,再做一次(15分钟内,每30秒一次)......跟随别人,再做一次......);
    5. 在这种情况下,当每个帐户的一个帖子刚刚开始工作时(队列中的内容>所以他做了)而另一个只是填满队列,我可以添加我的功能手动任务(现在,我所有的accs,再次讨论这个人或跟随这个人), - 在每个队列中我都会添加一个新任务,然后每个线程尽快执行。

      所以,我的目标(目标),制作一个应用程序,每个帐户都应该完成他的工作,我可以选择告诉他们,下一步该做什么(改变他们的任务优先级)。

      我想问你,我是关于队列的2个线程是正确的(可能它应该是一个全局队列,线程使用帐户将其任务作为标记(分隔符)),也许,你可以告诉我一些我的任务。

      修改 而那里最大的问题是,如果我制作20个账号,但只有5个线程(限制) - 所以它不应该是1个acc-> 2个线程,应该是1个线程来填充队列(看起来比如全球),以及其他用于制作工作 - 但他们可以为一个帐户制作作品(一个帖子制作twite,其他帖子开始关注用户),这使我在一个角落, - 不能理解,应该怎样我整理了所有这些主题。

      Edit2:还有一个问题:执行时间。我是否应该检查每个线程时间,什么时候应该执行任务(计划我的任务的下一个小时)(现在23:55,这个任务将在23:59执行, - 接下来是什么?跳过?睡觉?)。

      如果我不检查时间和队列中只添加任务,那就准备执行了,我再次遇到一个帐户问题 - 一个任务(设置添加两秒15秒延迟,添加retwite 20秒延迟,跟随人60秒延迟)=>每个minuit我将有3个任务在队列中,但应该逐个执行(我有100个帐户,但只有5个工作线程(他们应该在彼此之间同步,什么帐户任务现在执行其他线程?))

      编辑3(让我们轻松一点): 好吧,让我们忘记推特。我要编写windows桌面应用程序,它应该在控制台中写一些东西(让它来自Bob',来自Bob' hi,来自鲍勃'。)

      我可以为我的程序添加很多名字(Bob,Jack,John,Dave,Scott)并查看这些名称应写的短语:

      1. 鲍勃只写鲍勃'你好。每15秒;
      2. 杰克写了一封来自Jack'每1分钟一次,再见杰克'每5秒;
      3. John写道,你好',' hi',' bye' +来自' John',5秒,10秒,15 秒;
      4. e.t.c。 (让它成为20个名字);
      5. 我希望,那个哎呀?

        所以,现在,当我启动我的应用程序时,这20个名字中的每一个都应该在控制台中编写他们的短语,但是在他们的时间和所有线程中使用它(让它限制为10个工作线程) - 应该只是一个任务每个名字的任务(如果一个帖子正在写杰克'你好,其他人不应该写杰克'再见,即使这个任务应该在01:42执行: 00.只有杰克完成了一项任务,其他任务才开始)。

        (+)另外,我应该有一个选项,为这些名字中的某人添加,以便尽快写出我想要的任何短语,因为他们最接近的任务已经完成。

        我希望,我会找到你的帮助。

        编辑4:所以:enter image description here

        请求帮助,现在我知道我需要什么,以及如何组织我的应用程序。

        终于实现:

        我使用自己的代码完成了我的任务,对我来说解决方案看起来最好。

        enter image description here

        因此,当我为每个帐户生成队列并将它们放在一个应用程序任务的全局队列中时,我将它传递给TimerCallBack函数,该函数检查队列中是否有任何准备执行的任务(但只检查event_elements [0]), - 如果是这样,设置isactive = true(对于个人队列,防止执行下一个事件,在上一个完成之前),并放置此队列,应该在迭代后执行每个第一个事件。

        然后,将它作为集合传递给Parallel.ForEach:

         Parallel.ForEach(readyToExec, new ParallelOptions { MaxDegreeOfParallelism = 5 }, eq =>
                    {
                        lock (eq)
                        {
                            QueueElement exec_elem = eq.elements[0];
                            Console.WriteLine(exec_elem.timeToExecute + ": " + eq.account.Name + " " + exec_elem.EventType);
                            exec_elem.timeToExecute = DateTime.Now + exec_elem.timeDelay;
                            eq.timeLastExecute = DateTime.Now;
                            eq.elements.Sort();
                            eq.isactive = false;
                        }
                    });
        

        顺便说一下,在添加要执行的事件之前添加了检查:

                foreach (var equeue in queue)
                {
                    if (!equeue.isactive && equeue.elements[0].readyToExecute)
                    {
                        if (DateTime.Now > equeue.timeLastExecute + TimeSpan.FromSeconds(5))
                        {
                            equeue.isactive = true;
                            readyToExec.Add(equeue);
                        }
                        else
                        {
                            equeue.elements[0].timeToExecute = DateTime.Now + TimeSpan.FromSeconds(5);
                        }
                    }
                }
        

        以手动延迟执行一个帐户的事件(例如,如果在00:00:00写入' Hello',下一个事件无法开始执行直到00:00 :05,即使执行时间为00:00:02)。

        所以,它对我很有用;)

1 个答案:

答案 0 :(得分:0)

我的第一个倾向是使用优先级队列和Timer。我就是这样做的。

首先,创建一个全局Stopwatch以维持应用程序时间。您不希望夏令时或其他时间更改弄乱您的时间,因为这可能会导致您在事件之间等待太长时间或使事件发生得太快。所以,你有的地方:

static Stopwatch ApplicationTime = Stopwatch.StartNew();

您还有一个Account课程,其中包含有关Twitter帐户的信息,最重要的是,该帐户的最后一次操作的时间:

class Account
{
    // information about the account goes here

    public TimeSpan LastEventTime { get; set; }
}

一个事件类,用于定义将要采取的操作。

class AccountEvent
{
    public Account acct { get; private set; }
    public TimeSpan dueTime { get; set; }
    public ActionType action { get; private set; } // follow, re-tweet, etc.

    // constructor, etc.
}

最后,按事件时间键入的优先级队列:

PriorityQueue<TimeSpan, AccountEvent> EventQueue = new Priority<TimeSpan, AccountEvent>();

不幸的是,.NET运行时库中没有内置优先级队列类。如果您进行Google搜索,可以找到几个。我多年前写的一篇是http://www.devsource.com/c/a/Languages/A-Priority-Queue-Implementation-in-C/。您可以在http://mischel.com/pubs/priqueue.zip获取最新来源。请注意,我的优先级队列不是并发的;你必须围绕它包装一个并发层。出于您的目的,在以任何方式访问队列之前使用简单的lock就足够了。

现在,如何使用所有这些东西:

假设您想每隔30分钟查看一个帐户。您可以像这样创建一个新的AccountEvent

var ev = new AccountEvent(userAccount, ApplicationTime.Elapsed.AddMinutes(30), ActionType.Follow);
EventQueue.Enqueue(ev.dueTime, ev);

现在,您需要从队列中删除项目并处理它们。这就是计时器进来的地方:

Timer eventTimer = new System.Threading.Timer(QueueProc, null, TimeSpan.FromSeconds(5), TimeSpan.FromMilliseconds(-1));

创建一个将在五秒内触发的计时器。它是一次性定时器而不是周期性定时器,这可以防止我们获得重叠的定时器滴答。

现在,您的计时器处理程序会检查队列中是否有需要处理的事件:

void QueueProc(object state)
{
    while (eventQueue.Count > 0 && eventQueue.Peek().dueTime < ApplicationTime.Elapsed)
    {
        AccountEvent ev = eventQueue.Dequeue();
        // process item. See comments below.
    }
    // reset the timer so that it will fire in five seconds
    eventTimer.Change(TimeSpan.FromSeconds(5), TimeSpan.FromMilliseconds(-1));
}

因为单个帐户可以有多个事件,所以事件处理有点复杂,但并不过分。您必须根据帐户的上次活动时间检查当前时间。如果最后一个事件太近了,那么在事件时间添加一些时间并用新时间将其重新放回队列。

要处理某个项目,您可以触发异步Web请求(我建议的内容),使用BackgroundWorker或触发TPL Task。关键是当异步请求完成时,完成回调所做的最后一件事就是更新下一个事件的时间并将该事件放回队列中。

我在这里略过了一些细节,但我认为你可以收集基本的想法:至少足以开始。