首页 新闻 搜索 专区 学院

C# WinForm 线程中调用Dispose()

0
悬赏园豆:30 [已解决问题] 解决于 2021-04-30 15:15

这段代码,如果单独跑,它虽然会卡住主线程,但是下面的资源回收Dispose()都可以用,一个视频内存占用也就100~200M

public void OpenVideo()//打开rtsp视频
        {
            Capture = new VideoCapture(rtspUrl);
          int count =0
            while (!StopFlag)
            {
                if (Capture == null || Capture.IsDisposed)
                {
                    break;
                }
                Mat mat = new Mat();
                bool res = Capture.Read(mat);//会阻塞线程
                if (!res || mat.Empty())
                {
                   count ++;
                    if(count >50)break;//可能链接无效,线程退出
                    mat.Dispose();
                    continue;
                }
                count =0; 
                if (SaveFlag && VideoWriter != null)
                {
                    VideoWriter.Write(mat);
                }
                if (PictureBox != null)
                {
                    System.Drawing.Image image = PictureBox.Image;
                    PictureBox.Image = BitmapConverter.ToBitmap(mat);
                    if (image != null)
                    {
                        image.Dispose();//释放以前的图片,不然吃内存
                    }
                }
                Cv2.WaitKey(1);
                
                mat.Dispose();
            }
            Capture.Dispose();
        }


public void CloseVideo(){//关闭rtsp视频
    StopFlag = true;
}

public void StartOpenVideo(){
     Thread th = new Thread(OpenVideo);
     th.Start();
}

然后我把他放进线程里,里面的Dispose()就不好使了,内存就开始狂飙,没几分钟就二十多个G

                线程里,这句话执行普遍在20—60ms,单独执行方法,这句话执行普遍7—13ms
                bool res = Capture.Read(mat);//会阻塞线程

我发现,修改Cv2.WaitKey(1);把那个1修改大一些,内存就涨得慢一些……那个和Thread.Sleep()不一样吗?

还是内存为啥涨啊?
这个该怎么解决?

小草上飞飞的主页 小草上飞飞 | 菜鸟二级 | 园豆:287
提问于:2021-03-03 17:44
< >
分享
最佳答案
0

你的StopFlag在循环中未发生改变,死循环,并且对象的初始化操作不建议放在循环内部

收获园豆:20
Evilloafer | 菜鸟二级 |园豆:224 | 2021-03-04 09:00

线程, 本来就是拿他来跑死循环的,Mat mat = new Mat();初始化不放循环里,好像这个东西不会被回收,我当初测试发现这个了,我再测试一次去

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:04

所以我的mat 在循环里手动Dispose了

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:06

放外面也不行

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:12

stopFlag我用来做停止线程的标志了,是这个类的成员变量

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:13

@小草上飞飞: 其实他不会走到mat.DIspose()的,bool res = Capture.Read(mat);这行代码需要等待,所以res的值不会马上给你,下边你有一个逻辑判断,if (!res || mat.Empty())
{
continue;
}
mat是空的,所以会不停地创建mat对象

Evilloafer | 园豆:224 (菜鸟二级) | 2021-03-04 09:18

@Evilloafer: 我现在修改了那段代码,同时修改了博问。

我在昨天也以为时这个问题,我添加了几处Dispose,但是还是不行。

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:30

@小草上飞飞: 你不要用Dispose了,
public void OpenVideo()//打开rtsp视频
{
Capture = new VideoCapture(rtspUrl);
int count =0
Mat mat = null;
while (!StopFlag)
{
if (Capture == null || Capture.IsDisposed)
{
break;
}

            if (mat == null)
            {
                mat = new Mat();
            }

            bool res = Capture.Read(mat);//会阻塞线程
            if (!res || mat.Empty())
            {
                count ++;
                if(count >50)break;//可能链接无效,线程退出
                continue;
            }
            count =0;
            if (SaveFlag && VideoWriter != null)
            {
                VideoWriter.Write(mat);
            }
            if (PictureBox != null)
            {
                System.Drawing.Image image = PictureBox.Image;
                PictureBox.Image = BitmapConverter.ToBitmap(mat);
                if (image != null)
                {
                    image.Dispose();//释放以前的图片,不然吃内存
                }
            }
            Cv2.WaitKey(1);

            mat.Dispose();
        }
        Capture.Dispose();
    }
Evilloafer | 园豆:224 (菜鸟二级) | 2021-03-04 09:35

@Evilloafer: 我试试

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:37

@小草上飞飞: 还有一个问题就是如果只需要读取一次的话,建议你改变stopFlag,停止死循环

Evilloafer | 园豆:224 (菜鸟二级) | 2021-03-04 09:39

@Evilloafer: 这个是播放视频的,每次只抓取一帧像,然后赋给picturebox

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:42

@Evilloafer:
这个也不行
难不成只能使用C++?

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:43

@小草上飞飞: SaveFlag、VideoWriter、PictureBox都是对象名吗

Evilloafer | 园豆:224 (菜鸟二级) | 2021-03-04 09:47

@Evilloafer: SaveFlag,bool型,是存.mp4文件的标志,VideoWriter 是将图片写入mp4文件的对象,PictureBox 就是抓取到的图要赋给这个图片框显示出来。都是这个类的成员变量

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:51

@Evilloafer: 内外网分离,只能拍照片了

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:53

@小草上飞飞: 你有调试吗,他能不能走到为PictureBox赋值,现在不行是指图片显示不出来?

Evilloafer | 园豆:224 (菜鸟二级) | 2021-03-04 09:54

@Evilloafer: 现在的问题是,内存占用特别多。
单独调用方法,放按钮事件里,事件方法最后一个调用,播放一个视频也就不到200M内存,
放线程里,内存使用随时间递增,几分钟就20多G,这谁也顶不住啊

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:57

@Evilloafer: 功能是ok的

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:57

@小草上飞飞: System.Drawing.Image image = PictureBox.Image;
PictureBox.Image = BitmapConverter.ToBitmap(mat);
if (image != null)
{
image.Dispose();//释放以前的图片,不然吃内存
}
这里的image对象没有一点用处

Evilloafer | 园豆:224 (菜鸟二级) | 2021-03-04 10:00

@Evilloafer: 为了释放掉

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 10:11

@小草上飞飞: 你试试
if(PictureBox.Image!=null) PictureBox.Image.Dispose();
PictureBox.Image = BitmapConverter.ToBitmap(mat);

Evilloafer | 园豆:224 (菜鸟二级) | 2021-03-04 10:12

@小草上飞飞: 如果还是不行,你试试这样写
public void OpenVideo()//打开rtsp视频
{
Capture = new VideoCapture(rtspUrl);
int count =0
Mat mat = null;
while (!StopFlag)
{
if (Capture == null || Capture.IsDisposed)
{
break;
}
if(mat == null)
{
mat = new Mat();
}
bool res = Capture.Read(mat);//会阻塞线程
if (!res || mat.Empty())
{
count ++;
if(count >50)break;//可能链接无效,线程退出
continue;
}
count =0;
if (SaveFlag && VideoWriter != null)
{
VideoWriter.Write(mat);
}
if (PictureBox != null)
{
if(PictureBox.Image != null) PictureBox.Image.Dispose();
PictureBox.Image = BitmapConverter.ToBitmap(mat);
}
Cv2.WaitKey(1);
mat=null;
}
Capture.Dispose();
}

Evilloafer | 园豆:224 (菜鸟二级) | 2021-03-04 10:23

@Evilloafer: 这样我干过了,不行,

if(PictureBox.Image != null) PictureBox.Image.Dispose();
PictureBox.Image = BitmapConverter.ToBitmap(mat);

这两行会直接让程序崩溃,非法访问内存

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 10:29

@小草上飞飞: 好吧

Evilloafer | 园豆:224 (菜鸟二级) | 2021-03-04 10:34

@Evilloafer: 这就是样例,我照着写的

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 10:38

@Evilloafer: 只不过把两个读取视频和录制视频两个类合并到一个类了,原生OpenCv也是这么写的,直接放线程中,但是C++为啥会回收,C#不会

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 10:41

@小草上飞飞: 你可以把PictureBox 那几行注释掉,排除下错误

slowstart | 园豆:504 (小虾三级) | 2021-03-04 10:53

@slowstart: 试了,我也单步调试的话,发现image有被Dispose,因为里面的属性全没了

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 10:56

@slowstart: 注释掉不影响结果,太骚了

小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 10:58
其他回答(2)
0

你是怎么使用StartOpenVideo这个方法的

收获园豆:5
通信的搞程序 | 园豆:1694 (小虾三级) | 2021-03-03 18:32

StartOpenVideo 这个方法放按钮Click的事件里面

支持(0) 反对(0) 小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-03 20:09

这俩一方法,都是在同一个位置被调用。因为我最开始没用线程,现在发现卡住主线程了,才启用线程,然后发现内存变了

支持(0) 反对(0) 小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 08:39

@小草上飞飞: 这个按钮只点一次的吧

支持(0) 反对(0) slowstart | 园豆:504 (小虾三级) | 2021-03-04 09:18

@slowstart: 点第二次会关闭第一次打开的线程

支持(0) 反对(0) 小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:19
0

在 while 循环中阻塞线程,不狂飙才快呢,线程池中的线程什么都不怕,就怕阻塞,建议使用异步方法

收获园豆:5
dudu | 园豆:37728 (高人七级) | 2021-03-03 20:24

主线程没有使用线程池,new Thread用了线程池

支持(0) 反对(0) dudu | 园豆:37728 (高人七级) | 2021-03-03 20:26

@dudu: 没懂

支持(0) 反对(0) 小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 08:07

@dudu: 讲细一点

支持(0) 反对(0) 小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 08:07

@dudu: 或者说怎么解决

支持(0) 反对(0) 小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 08:11

@dudu: 你的意思是 bool res = Capture.Read(mat);//会阻塞线程这一行阻塞了线程?

支持(0) 反对(0) 小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 08:13

@小草上飞飞: 注释中不是写了“会阻塞线程”吗?

支持(0) 反对(0) dudu | 园豆:37728 (高人七级) | 2021-03-04 08:39

@dudu: 是的,没有下一张图,他就会在那里等着

支持(0) 反对(0) 小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 08:40

@dudu: 有没有啥解决办法?这个是OpenCvSharp的例子,不是很会改它

支持(0) 反对(0) 小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 08:41

@dudu: 我没有找到 线程中调用阻塞线程方法 会增加内存的解释或者相关的,我不是很懂这块,能讲细致点嘛?

支持(0) 反对(0) 小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 08:43

@小草上飞飞: 下面的代码有点问题,应该先 Dispose 后 break

if(count >50)break;//可能链接无效,线程退出
mat.Dispose();
支持(0) 反对(0) dudu | 园豆:37728 (高人七级) | 2021-03-04 09:32

@dudu: 这只能影响到一个mat,一张图片大小,而且break之后,线程方法就执行完了,该回收了,里面变量也就回收了

支持(0) 反对(0) 小草上飞飞 | 园豆:287 (菜鸟二级) | 2021-03-04 09:36
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册