如何正确地每分钟执行一次线程?
Posted
技术标签:
【中文标题】如何正确地每分钟执行一次线程?【英文标题】:How to execute threads every min correctly? 【发布时间】:2016-07-09 10:30:58 【问题描述】:这个问题分为两部分。 我有一个控制台应用程序,它从多个服务器获取信息并将此信息保存到数据库。为了使这同时执行,我使用了线程。我试图让这个执行每分钟自动执行一次。
搜索***我发现这可以工作:
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();
我怎样才能做到这一点?
【问题讨论】:
'TimeSpan.FromMinutes(1).Seconds' 返回 0。你想要返回 60 的 'TimeSpan.FromMinutes(1).TotalSeconds'。 我会使用TimeSpan
重载。 msdn.microsoft.com/en-us/library/ah1h85ch(v=vs.110).aspx
TimeSpan.FromSeconds(60).Seconds
怎么样?!
【参考方案1】:
在您的第一个问题中,使用 .Seconds 只会返回秒值,但您将分钟值定义为 0.5,因此秒将始终为零。
如果要返回需要使用 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.
【讨论】:
试过了,它每秒运行一次,直到我有 475 个线程,然后它崩溃了 所以执行时间比间隔时间长。那是性能问题。你应该试试别的…… @Ra3IDeN 查看我更新的答案的第二部分。 好吧,我认为这实际上是我正在寻找的。虽然我很感激一个简单的澄清。我复制了您答案的第二部分,并认为执行将每 30 秒发生一次。我已经让应用程序运行了 3 分钟,它只执行了一次。我目前的代码: (int)TimeSpan.FromMinutes(1000 * 60 * 0.5).TotalSeconds) @CathalMF 老实说,我不知道您为什么还要费心将TimeSpan
转换为整数,而Timer
类已经接受TimeSpan
。如果您按原样使用它,就不会产生混淆。【参考方案2】:
基本上,计时器完全按照它应该做的事情:每 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);
【讨论】:
这不起作用。一旦我没有在调试器中运行它,第一个解决方案就给了我一个超出范围异常的索引。第二种解决方案只运行线程一次然后停止。 @Ra3IDeN 我确实忘记了第一个示例中的线程启动;现在已经解决了。第二种解决方案按原样工作。如果您得到一个超出范围异常的索引,您正在更改线程中的集合 - 在任何情况下都是不允许的,您正在创建一个竞争条件! 至于您声称它没有不行,我刚刚创建了一个最小的测试用例,它工作得很好;请自行检查。以上是关于如何正确地每分钟执行一次线程?的主要内容,如果未能解决你的问题,请参考以下文章
如何在java中实现 sql语15分钟执行一次 希望说详细一下