如何正确地每分钟执行一次线程?

How to execute threads every min correctly?

这个问题分为两部分。 我有一个控制台应用程序,它从多个服务器获取信息并将此信息保存到数据库中。为了使它同时执行,我使用了线程。我试图每分钟自动执行一次。

搜索 Whosebug 我发现这可行:

   var timer = new System.Threading.Timer((e) =>
        {
            var models = ServerHandler.GetServerModels();

            foreach (var m in models)
            {
                ServerHandler.MakeThreads(m);
            }

            Console.WriteLine("Running...");
            Console.WriteLine("Press 'X' to exit or close the window, i : " + i);

            i++;


        }, null, 0, TimeSpan.FromMinutes(1).Seconds);

然而,这并没有像预期的那样工作,它只执行一次。例如,如果我更改为:

TimeSpan.FromMinutes(0.5).Seconds

或者:

TimeSpan.FromSeconds(30).Seconds

然后就可以了。 我做错了什么?

本题第二部分:

当这真的像我上面展示的那样工作时,会发生其他事情。 该进程连续运行,在 474 个线程后崩溃并表示系统内存不足。

我尝试为此使用线程睡眠,但是当我这样做时,它在运行一次后停止执行。

包括这个如果它可能有帮助:

public static void MakeThreads(ServerModel model)
{
    Thread thread = new Thread(() => SaveServerInfo(model));
    thread.Start();
    //Thread.Sleep(1);
    //thread.Join();

}

我怎样才能使这个工作?

在您的第一个问题中,使用 .Seconds 只会 return 秒值,但您将分钟值定义为 .5,因此秒将始终为零。

如果你想return秒你需要使用TotalSeconds

TimeSpan.FromMinutes(0.5).TotalSeconds

并且在您使用的时间跨度中,您应该定义毫秒。所以你得到了大量的线程,因为它每 30 毫秒而不是每 30000 毫秒 运行。

所以使用

TimeSpan.FromMinutes(0.5).TotalMilliseconds

或者我总是觉得更简单的方法

(int)(1000 * 60 * 0.5)  // Then you just replace the 0.5 with the number of seconds. 

基本上,计时器会准确执行它应该执行的操作:运行 您的代码每 0.5 秒执行一次。 :) 在你的情况下,这是个问题...

(请检查语法错误等,我是用记事本写的)

长解

您的问题似乎是您无法控制线程。以下是我的解决方法:(这个长解决方案或多或少展示了它是如何工作的)

while (true)
{    
    // we want to run it again in 0.5 seconds.
    DateTime start = DateTime.UtcNow.AddSeconds(0.5); 

    Thread[] threads = new Thread[models.Count];
    for (int i=0; i<models.Count; ++i)
    {
        threads[i] = new Thread((a) => SaveServerInfo((ServerModel)a));
        threads[i].Start(models[i]);
    }

    for (int i=0; i<models.Count; ++i)
    {
        threads[i].Join();
    }

    DateTime current = DateTime.UtcNow;
    if (current < start)
    {
        Thread.Sleep(start.Subtract(current));
    }

}

简解

但是,这也可能会给您带来问题:您可能会生成太多线程。这可以通过称为线程池的机制来解决。事实证明,有一个简单的方法可以解决这个问题:

static void DoStuff(string s)
{
    // change to a value > 0.5 as well to ensure everything works
    Thread.Sleep(TimeSpan.FromSeconds(0.1)); 
    Console.WriteLine(s);
}

static void Handle(List<string> models)
{
    while (true)
    {
        // we want to run it again in 0.5 seconds.
        DateTime start = DateTime.UtcNow.AddSeconds(0.5);

        Parallel.ForEach(models, (a) => DoStuff(a));
        DateTime current = DateTime.UtcNow;
        if (current < start)
        {
            Thread.Sleep(start.Subtract(current));
        }
    }
}

static void Main(string[] args)
{
    List<string> models = new List<string>();
    for (int i = 0; i < 10; ++i)
    {
        models.Add(i.ToString());
    }

    Handle(models);

}