如何限制Timer挂起请求

时间:2014-07-10 16:44:50

标签: c# wpf timer

在C#Wpf Application应用程序中,我创建了一个名为System.Timers.Timer的计时器。我将定时器间隔设置为1秒。在Timer方法内部,我通过HTTP POST将数据发布到名为"www.posttestserver.com"的Web服务器。我使用的Http邮政编码是:

try
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(http://posttestserver.com/post.php?dir=xxx);
    string data="This is to test Http post service";
    request.Method = "POST";
    request.ProtocolVersion = HttpVersion.Version11;
    byte[] byteArray = Encoding.UTF8.GetBytes(data);
    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = byteArray.Length;
    request.Proxy = null;
    using (Stream dataStream = request.GetRequestStream())
    {
        dataStream.Write(byteArray, 0, byteArray.Length);
        dataStream.Flush();
        dataStream.Close();
    }
    var response = request.GetResponse() as HttpWebResponse;
    response.Close();
    request.Abort();
    request = null;
}
catch (Exception ex)
{
    myTimer .Close();
     MessageBox.Show("HttpPost:" + ex.Message);
}

每件事都很好。但问题是 当我删除我的主机的网络电缆(HttpWebRequest)WebRequest.Create()时发布时,我的计时器正在运行请求和计时器请求处于挂起状态时占用更多时间意味着突然我得到了一堆例外。 可以任何人帮助我如何解决/限制计时器待处理请求。

2 个答案:

答案 0 :(得分:1)

至少有三种不同的方法可以解决这个问题。

最简单的方法是在输入回调时禁用计时器,然后在退出时重新启用它。那就是:

void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
    timer1.Enabled = false;
    try
    {
        // do stuff
    }
    finally
    {
        timer1.Enabled = true;
    }
}

这将防止多个并发事件,除非在特殊情况下(当系统严重超载时)。

使用Monitor API,您可以使用锁执行基本相同的操作。但是,在你正在进行网络通话的整个过程中持一把锁可能是一个坏主意。

您还可以将计时器设为一次,这样它只会打勾一次,然后自动禁用。然后Elapsed事件处理程序重新启用它。为此,您可以像平常一样初始化计时器,但是设置AutoReset = false;并且您的事件处理程序如下所示:

void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
    try
    {
        // do stuff
    }
    finally
    {
        timer1.Enabled = true;
    }
}

这可以可靠地防止多个并发调用。但是,计时器不再每秒打一次。相反,它在前一个处理程序完成后返回一秒钟。因此,如果您将计时器间隔设置为1秒并且执行POST需要500毫秒,那么效果将是您每1.5秒发出一个POST请求。在大多数情况下,除非您绝对必须每秒发出一个请求,否则此行为就可以了。

更新

如果你想加快速度,有几种可能性。一种是使用我上面提到的lock方法。这将允许每秒一次的滴答,这可能会使吞吐量略微加快。它看起来像这样:

private readonly object timerLock = new object();
void timer1_Elapsed(...)
{
    bool lockTaken;
    Monitor.TryEnter(timerLock, out lockTaken);
    if (!lockTaken) return;
    // do stuff here
    // and release the lock
    Monitor.Exit(timerLock);
}

其他可能性是使用Semaphore允许多个并发请求,最多可达一些固定数量。可能2或3就足够了。或者您可以创建请求队列。每个计时器tick只是将请求发布到队列,并且单独的线程尽可能快地为队列提供服务。 总是其他方式做事,有些比其他方式更复杂,都有各自的优点和缺点。

答案 1 :(得分:0)

您可以做几件事:

  1. 在尝试阻止之前将myTimer.Enabled设置为false,并在finally阻止中启用它。在Timer.Close中使用catch将释放资源,但只会在运行垃圾收集后处置Timer。使用Timer.Enabled = false将其停用

  2. 使用WebRequest.Timeout属性将您的请求超时限制为更短的时间范围

  3. 除此之外,我希望您的间隔超时一秒仅用于测试目的。