1

创建嵌套的一般做法是System.Timer什么?基本上,我需要一个每 10 秒无限期运行的父计时器,以及一个每秒运行确定时间长度的子计时器。我遇到的问题是如何在完成后正确退出子计时器。

这是一个简单的示例 - 我有一个父计时器,其对应的ElapsedEventHandler. 在此事件中,我调用了返回对象列表的第三方 API。现在我有了这些对象,我需要对这些对象中的每一个进行额外的 API 调用,每秒调用一次(输入子计时器)。假设第一个 API 调用带回 30 个对象。我希望ElapsedEventHandler子计时器只运行 30 次,然后退出。我只是不确定如何实现这一点。

我的计时器的原因是因为这个第三方 API 施加了速度限制。

4

3 回答 3

1

听起来您只需要一个全局处理队列,并为 API 供应商强加给您的任何节流间隔设置一个计时器。所有 API 请求都将排队,任何需要更多子请求的 API 请求的结果也将排队。您有一个计时器,它每秒处理 Queue 中的下一项。

这是我正在谈论的示例。我会为每个 API 请求创建一个子类,以便 a) 正确处理请求并 b) 根据需要将子任务排入队列,如果需要,从当前 API 请求的结果设置子任务的状态:

void Main()
{
    Timer t = new Timer(ProcessNextItem, null, 0, 1000);
}

public void ProcessNextItem(object o){
    var nextTask = Tasks.Take();

    nextTask.Process(); // Process does the API calls and enqueues new tasks onto Tasks, and sets state of child tasks as needed
}

public static BlockingCollection<APIRequestTask> Tasks = new BlockingCollection<APIRequestTask>(new ConcurrentQueue<APIRequestTask>());

public abstract class APIRequestTask{
    public void Process(){}
    public object State {get;set;}
}
于 2013-10-10T14:12:27.243 回答
1

您需要每 10 秒获取一次,还是在处理完所有数据后获取?您提到每 10 秒获取一次数据,但返回 30 个项目。以这种速度,您将继续分配内存,因为它永远无法赶上。

1 秒计时器然后增加一个静态计数器以每 10 次迭代获取数据。检索到数据后,每秒都会将每个项目从堆栈中弹出。

using System;
using System.Collections.Generic;
using System.Threading;

namespace TimedFetch
{
    class Program
    {
        static System.Threading.Timer workTimer;
        static Stack<string> itemList;
        public static AutoResetEvent fetchDataEvent;
        static int eventCounter;

        public class ExternalDatamanager
        {
            public void FetchData()
            {
                DateTime startTime = DateTime.Now;

                do
                {
                    FetchHeaderData();
                    fetchDataEvent.WaitOne();
                    Console.WriteLine("{0} FetchData Signaled! List size {1}", DateTime.Now.ToLongTimeString(), itemList.Count);
                } while(true);
            }

            private static void FetchHeaderData()
            {
                // replace this with your method of retrieving data
                Console.WriteLine("{0} FetchHeaderData", DateTime.Now.ToLongTimeString());
                DateTime timeObject = DateTime.Now;
                for (int i = 0; i < 30; i++)
                {
                    lock (itemList)
                    {
                        itemList.Push(timeObject.ToLongTimeString());
                        timeObject = timeObject.AddSeconds(1);
                    }
               }
            }
        }

        static void Main(string[] args)
        {
            eventCounter = 0;
            itemList = new Stack<string>();
            fetchDataEvent = new AutoResetEvent(false);

            ExternalDatamanager dataManager = new ExternalDatamanager();
            Thread workerThread = new Thread(new ThreadStart(dataManager.FetchData));
            workerThread.Start();

            workTimer = new System.Threading.Timer(workTimer_Elapsed, null, 0, 1000);
        }

        // executes once a second
        private static void workTimer_Elapsed(Object state)
        {
            Console.WriteLine("{0} Fire!", DateTime.Now.ToLongTimeString());
            Interlocked.Increment(ref eventCounter);

            lock (itemList)
            {
                // every 10 seconds
                if (itemList.Count > 0)
                    FetchDetail(itemList.Pop());
                // else ??
                // { ??
                if (eventCounter % 10 == 0)
                    fetchDataEvent.Set();
                // } ??
            }
        }

        private static void FetchDetail(string headerRecord)
        {
            Console.WriteLine("{0} Fetch detail for {1}", DateTime.Now.ToLongTimeString(), headerRecord);
        }
    }
}
于 2013-10-13T07:35:00.743 回答
0

如果 API 调用者在单独的线程上运行,请忘记使用子计时器。只需让线程在两次拉动之间休眠一秒钟。

像这样的东西:

foreach(var item in apiObjectsToGet)
{
    api.GetItem(item);
    Thread.Sleep(1000);
}
于 2013-10-10T14:10:06.207 回答