【问题标题】:Using statement in Task在任务中使用语句
【发布时间】:2013-11-29 21:52:51
【问题描述】:

在 ASP.NET Web API 控制器中,我想返回一个图像。要流式传输图像,我需要MemoryStream。通常我会将它包装在using 语句中,以确保它在之后得到正确处理。但是,由于它在 Task 中异步执行,因此不起作用:

public class ImagesController : ApiController
{
    private HttpContent GetPngBitmap(Stream stream)
    {
        var pngBitmapEncoder = new PngBitmapEncoder();
        pngBitmapEncoder.Save(stream);
        stream.Seek(0, SeekOrigin.Begin);
        return new StreamContent(stream);
    }

    // GET api/images
    public Task<HttpResponseMessage> Get(string id, string path)
    {            
        //do stuff           
        return Task.Factory.StartNew(() =>
        {
            var stream = new MemoryStream(); //as it's asynchronous we can't use a using statement here!
            {
                var response = new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = GetPngBitmap(stream)
                };
                response.Content.Headers.ContentType =
                    new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
                return response;
            }
            //how can I dispose stream???
        });
    }
}

【问题讨论】:

  • GetPngBitmap 是什么类型,这在这种情况下很重要。当‘png bitmap’不再引用流时,dispose流是有效的。
  • 在你调用GetPnGBitmap()方法后调用Dispose on stream?或者使用 try finally 块并在 finally 中调用 Dispose,或者在您返回响应之前调用 Dispose。我不明白问题是什么?为什么你不能使用 using?
  • Web API 托管层负责在写出响应后在此处处理流,因此您不必担心自己关闭流……还有方法调用 GetPngBitmap 做什么?是否将内容从文件复制到内存流?如果是,则无需再次创建内存流,只需将文件流直接提供给 Content 作为StreamContent ..
  • @Caramiriel:我试图处理它,但没有用。
  • 感谢您的代码...是的,流将被 Web API 的主机层关闭,因此您无需明确地从您身边做任何事情...Web API 的托管层从您的内存流到网络流,然后关闭源流(您的内存流)...这对于所有响应来说通常是相同的行为..

标签: c# asp.net-web-api task-parallel-library using


【解决方案1】:
  1. MemoryStream 是实现IDisposable 的类之一,因为它们的基类实现了。 MemoryStream 不保存任何资源(托管内存除外),因此实际上没有必要处理它。
  2. HttpResponseMessage 是一次性的。这意味着当它发送整个响应时,该对象被释放。这样做会处理包含的HttpContent,在StreamContent 的情况下会处理底层的Stream。因此,即使您有一个应该处理的 Stream,在这种情况下您也不必担心。

【讨论】:

    【解决方案2】:

    使用“使用”然后它会自动处理。

    public class ImagesController : ApiController
    {
        // GET api/images
        public Task<HttpResponseMessage> Get(string id, string path)
        {            
            //do stuff           
            return Task.Factory.StartNew(() =>
            {
               using( stream = new MemoryStream()) //as it's asynchronous we can't use a using statement here!
               {
                {
                    var response = new HttpResponseMessage(HttpStatusCode.OK)
                    {
                        Content = GetPngBitmap(stream)
                    };
                    response.Content.Headers.ContentType =
                        new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
                    return response;
                }
                //how can I dispose stream???
               }
            });
        }
    }
    

    【讨论】:

    • 您是否阅读了您修改的那一行之后的评论?为什么会这样?
    • 因为当您使用“使用”时,.net 将在“}”之后调用 .Dispose() 来处理对象。如果它之前存在(返回/异常),那么它无论如何都会发生。您应该阅读有关“使用”块的更多信息,看看它的作用。谷歌它
    • 但是 OP 声明 在这种情况下 不能使用“使用”。你确定它可以在这里使用吗? (我不知道这两种方式,没有足够的异步经验)
    • 也许尝试另一种方式返回照片?在我的 asp.net web api 中,我以不同的方式返回了图像
    【解决方案3】:

    也许返回一个Tuple&lt;HttpResponseMessage, Stream&gt; 并附加一个继续任务来处理Stream

    public Task<Tuple<HttpResponseMessage,Stream>> Get(string id, string path)
    {            
        //do stuff           
        return Task.Factory.StartNew(() =>
        {
            var stream = new MemoryStream();
            {
                var response = new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = GetPngBitmap(stream)
                };
                response.Content.Headers.ContentType =
                    new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
                return Tuple.Create(response, stream);
            }
        })
        .ContinueWith(t => t.Result.Item2.Close());
    }
    

    【讨论】:

    • 1.这不会编译,您将返回非泛型 Task。 2. 我真的不明白返回Tuple 应该完成什么。 3. Web API 是否知道当返回一个Tuple 时,它应该访问它的Item1?我对此表示怀疑。
    • @svick, 1. 响应是一个建议,2. 在结果旁边,应该返回对 Stream 的引用,在该引用上调用 Dispose() 之后 从流中读取。 3. 我不知道 Web API,这让我再次想到 1。 - 我提供了一个建议,而不是一个解决方案。我不知道项目的所有方面使其成为解决方案。
    【解决方案4】:

    也许这听起来很幼稚,但你不能在任务结束时处理它吗?

    var stream = new MemoryStream();
    //use the stream...
    stream.Dispose();
    

    edit: 或“Close()”,在 MemoryStream 上也是一样的。

    【讨论】:

    • 不,我不能。如果可能的话,我可以使用 using 语句。
    • 抱歉,刚刚看到return语句在里面
    【解决方案5】:

    你可以这样做。这将首先执行您的 initialRun,在异步调用之后,返回并执行 continue with。

            Task<System.IO.MemoryStream> initialRun = new Task<System.IO.MemoryStream>(() =>
            {
                System.IO.MemoryStream stream = new System.IO.MemoryStream();
                // use stream
                return stream;
            });
            initialRun.ContinueWith(new Action<Task<System.IO.MemoryStream>>((stream) =>
            {
                stream.Dispose();
            }), 
            TaskScheduler.FromCurrentSynchronizationContext());
            initialRun.Start();
    

    【讨论】:

    • 也许我没抓住重点,但最后我想返回一个 HttpResponseMessage。
    • @Dunken,你可以创建无限的 continuewith,随心所欲,这个只是演示了你在执行后如何处理一些东西
    • 我知道,但基本上我必须返回一个 HttpResponse,然后我想处理一个流......我不知道如何使用 ContinueWith() 来做到这一点
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多