使用新线程从Web加载图像

时间:2014-02-24 00:19:07

标签: c# multithreading visual-studio-2013 webrequest system.net

我正在尝试将网络服务器中的图像加载到pictureBox中。为了不阻止表格,直到图片被加载我开始一个新的线程,它在LoadPicture() - 函数上工作,直到所有工作完成。

所有内容都将在MouseHover-Event上启动,因此可以在短时间内多次触发,因为默认的WindowsHoverTime是180ms并且无法更改(我在那里搜索了很多)。 这是功能:

public void LoadPicture(string url)
{
    try
    {
        WebRequest req = HttpWebRequest.Create(url);
        req.Timeout = 3500;
        req.Method = "HEAD";
        using (WebResponse resp = req.GetResponse())
        {
            int ContentLength;
            if (int.TryParse(resp.Headers.Get("Content-Length"), out ContentLength))
            {
                string ContentType = resp.Headers.Get("Content-Type");
                if (ContentLength < 1048576 && ContentType == "image/png")
                {
                    this.pictureBox1.Load(url);
                }
                else
                {
                    this.pictureBox1.Image = mainprogram.Properties.Resources.sample;
                }
            }
        }
    }
    catch { this.pictureBox1.Image = mainprogram.Properties.Resources.sample; }
}

如果url包含一个小于1MB的png文件,则应将其加载到pictureBox1中,否则,将加载资源中的deafult图像。

线程和调用:

namespace mainprogram
{
    public partial class Form1 : Form
    {
        Thread threadworker;
        .
        .
        .
        private void button1_MouseHover(object sender, EventArgs e)
        {
            if (threadworker.IsAlive == false)
            {
                threadworker = new Thread(() => LoadPicture(url));
                threadworker.Start();
            }
        }

现在出现问题:线程几乎一直都会失败。 Catch {}将执行9/10次。主要是在WebResponse resp = req.GetResponse()崩溃。如果我在同一个线程中使用该函数(不启动新的),它将加载正常,但GUI将停止响应3秒,直到图片被加载。

编辑:它有时会工作,所以我不确定我做错了什么。

2 个答案:

答案 0 :(得分:0)

使用async

举例说明SLaks优秀评论,这是MSDN上如何使用async / await for web http://msdn.microsoft.com/en-us/library/hh300224.aspx#BKMK_ConvertGtBtArr

的漫步

this answer to a similar question上async / await的工作方式和好处进行了大量解释。

使用.NET 4.0

还有一个GetResponse的异步版本可用于.NET 4.0(及更低版本),称为BeginGetResponse。它记录在MSDN http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.begingetresponse(v=vs.100).aspx上,但该页面上的示例很糟糕,因为它阻止了使用事件。你想要的是在UI线程中调用你的回调。 async / await会自动执行此操作。使用回调,您可能必须手动将回调推迟到UI syncContext。

说明

无论如何,所有这些只有在实际问题是访问冲突时才有用,因为您尝试在非UI线程中绘制所请求的图像和/或阻止UI。如果GetResponse因DNS问题而引发异常,错误403/404,超时或类似情况,您应该投资找出原因。

答案 1 :(得分:0)

经过大量的研究和麻烦,我得到了它的工作。 首先,我要感谢SLaks的评论。我使用了async / await + GetResponseAsync,它完美无缺:

public async void LoadPicture(string url)
{
    bool worker = await GetURLContentsAsync(url);
}

private async Task<bool> GetURLContentsAsync(string url)
{
    var webReq = (HttpWebRequest)WebRequest.Create(url);             
    using (WebResponse response = await webReq.GetResponseAsync())
    {
        .
        .
        .

不幸的是,GetResponseAsync仅在.NET Framework 4.5中可用,因此我尝试使用与此处不同的BeginGetResponse方法: https://www.informit.com/guides/content.aspx?g=dotnet&seqNum=583 但它阻止了UI线程(非异步)或者无法加载图片3/4次(相同的图像)。

所以我最终再次使用线程:

private void LoadPicture(string url)
{
    threadworker = new Thread(() => pictureBox1.Image = getImgFromUrl(url));
    threadworker.Start();
} 

public Bitmap getImgFromUrl(string url)
{
    Bitmap bmp = null;
    try
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        try
        {
            bmp = new Bitmap(response.GetResponseStream());
            .
            .
            .

正如你所看到的,它几乎与问题帖子中的代码相同,我发现threadworker.IsAlive是罪魁祸首,pictureBox1.Load()比将流写入临时BitMap文件需要更长的时间然后加载它。