Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么后台线程中的图形操作会阻止主UI线程中的图形操作?_C#_Multithreading_Gdi+_Blocking - Fatal编程技术网

C# 为什么后台线程中的图形操作会阻止主UI线程中的图形操作?

C# 为什么后台线程中的图形操作会阻止主UI线程中的图形操作?,c#,multithreading,gdi+,blocking,C#,Multithreading,Gdi+,Blocking,我有一个背景线程,在给定文件夹中创建图像的灰度缩略图。我看到的问题是,后台线程中的Graphics.DrawImage()调用似乎在某种程度上阻止了主UI线程上的图形操作 我可能误解了我在这里看到的,直到今晚晚些时候才有机会做任何深入的剖析,尽管我不希望能找到太多 我试着想出一个尽可能小的复制案例。如果用下面的表单替换默认项目中的表单(并且在文件夹中有一些图像可供测试),您会注意到动画标签在窗口上来回反弹时会结巴。但是,如果您取消注释顶部的#define,以便子控件设置动画而不是重画窗口内容,那

我有一个背景线程,在给定文件夹中创建图像的灰度缩略图。我看到的问题是,后台线程中的Graphics.DrawImage()调用似乎在某种程度上阻止了主UI线程上的图形操作

我可能误解了我在这里看到的,直到今晚晚些时候才有机会做任何深入的剖析,尽管我不希望能找到太多

我试着想出一个尽可能小的复制案例。如果用下面的表单替换默认项目中的表单(并且在文件夹中有一些图像可供测试),您会注意到动画标签在窗口上来回反弹时会结巴。但是,如果您取消注释顶部的#define,以便子控件设置动画而不是重画窗口内容,那么它将运行得非常平稳

有人能看到我在这里做错了什么,或者帮我找出如何在更新循环中避免这种口吃吗

//#define USE_LABEL_CONTROL

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using Timer = System.Windows.Forms.Timer;

namespace ThreadTest
{
    public partial class Form1 : Form
    {
        private const string ImageFolder = "c:\\pics";
        private const string ImageType = "*.jpg";

        public Form1()
        {
            InitializeComponent();
        }

        protected override void OnLoad(EventArgs e)
        {
            this.Size = new Size(300, 300);

            string[] ImageFiles = Directory.GetFiles(ImageFolder, 
                                                        ImageType, 
                                                        SearchOption.AllDirectories);

            // kick off a thread to create grayscale thumbnails of all images
            this.thumbnailThread = new Thread(this.thumbnailThreadFunc);
            this.thumbnailThread.Priority = ThreadPriority.Lowest;
            this.thumbnailThread.Start(ImageFiles);

            // set a timer to start us off...
            this.startTimer = new Timer();
            this.startTimer.Interval = 500;
            this.startTimer.Tick += this.startTimer_Tick;
            this.startTimer.Start();

#if USE_LABEL_CONTROL
            this.label.Location = this.labelRect.Location;
            this.label.Size = this.labelRect.Size;
            this.label.Text = "Loaded: 0";
            this.label.BorderStyle = BorderStyle.FixedSingle;
            this.Controls.Add(this.label);
#endif

            base.OnLoad(e);
        }

        void startTimer_Tick(object sender, EventArgs e)
        {
            // kill the timer
            this.startTimer.Stop();

            // update ourself in a loop
            while (this.IsHandleCreated)
            {
                int NextTick = Environment.TickCount + 50;

                // update the label position
                this.labelRect.Offset(this.currentLabelDirection, 0);
                if (this.labelRect.Right == this.ClientRectangle.Right ||
                    this.labelRect.Left == 0)
                {
                    this.currentLabelDirection = -this.currentLabelDirection;
                }

                // update the display
#if USE_LABEL_CONTROL
                this.label.Text = "Loaded: " + this.thumbs.Count;
                this.label.Location = this.labelRect.Location;
#else
                using (Graphics Dest = this.CreateGraphics())
                {
                    this.redrawControl(Dest, this.ClientRectangle);
                }
#endif

                Application.DoEvents();
                Thread.Sleep(Math.Max(0, NextTick - Environment.TickCount));
            }
        }

        private void thumbnailThreadFunc(object ThreadData)
        {
            string[] ImageFiles = (string[]) ThreadData;
            foreach (string ImageFile in ImageFiles)
            {
                if (!this.IsHandleCreated)
                {
                    return;
                }

                using (Image SrcImg = Image.FromFile(ImageFile))
                {
                    Rectangle SrcRect = new Rectangle(Point.Empty, SrcImg.Size);

                    Rectangle DstRect = new Rectangle(Point.Empty, new Size(300, 200));
                    Bitmap DstImg = new Bitmap(DstRect.Width, DstRect.Height);
                    using (Graphics Dst = Graphics.FromImage(DstImg))
                    {
                        using (ImageAttributes Attrib = new ImageAttributes())
                        {
                            Attrib.SetColorMatrix(this.grayScaleMatrix);
                            Dst.DrawImage(SrcImg, 
                                            DstRect, 
                                            0, 0, SrcRect.Width, SrcRect.Height, 
                                            GraphicsUnit.Pixel, 
                                            Attrib);
                        }
                    }

                    lock (this.thumbs)
                    {
                        this.thumbs.Add(DstImg);
                    }
                }
            }
        }

#if !USE_LABEL_CONTROL
        private void redrawControl (Graphics Dest, Rectangle UpdateRect)
        {
            Bitmap OffscreenImg = new Bitmap(this.ClientRectangle.Width, 
                                                this.ClientRectangle.Height);
            using (Graphics Offscreen = Graphics.FromImage(OffscreenImg))
            {
                Offscreen.FillRectangle(Brushes.White, this.ClientRectangle);
                Offscreen.DrawRectangle(Pens.Black, this.labelRect);
                Offscreen.DrawString("Loaded: " + this.thumbs.Count,
                                        SystemFonts.MenuFont,
                                        Brushes.Black,
                                        this.labelRect);
            }
            Dest.DrawImageUnscaled(OffscreenImg, 0, 0);
            OffscreenImg.Dispose();
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            return;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            this.redrawControl(e.Graphics, e.ClipRectangle);
        }
#endif


        private ColorMatrix grayScaleMatrix = new ColorMatrix(new float[][] 
                                                        {
                                                            new float[] {.3f, .3f, .3f, 0, 0},
                                                            new float[] {.59f, .59f, .59f, 0, 0},
                                                            new float[] {.11f, .11f, .11f, 0, 0},
                                                            new float[] {0, 0, 0, 1, 0},
                                                            new float[] {0, 0, 0, 0, 1}
                                                        });
        private Thread thumbnailThread;
        private Timer startTimer;
        private List<Bitmap> thumbs = new List<Bitmap>();
        private Label label = new Label();
        private int currentLabelDirection = 1;
        private Rectangle labelRect = new Rectangle(0, 125, 75, 20);
    }
}
/#定义使用标签控制
使用制度;
使用System.Collections.Generic;
使用系统图;
使用系统、绘图、成像;
使用System.IO;
使用系统线程;
使用System.Windows.Forms;
使用Timer=System.Windows.Forms.Timer;
命名空间线程测试
{
公共部分类Form1:Form
{
private const string ImageFolder=“c:\\pics”;
私有常量字符串ImageType=“*.jpg”;
公共表格1()
{
初始化组件();
}
受保护的覆盖无效加载(事件参数e)
{
该尺寸=新尺寸(300300);
string[]ImageFiles=目录.GetFiles(ImageFolder,
图像类型,
SearchOption.AllDirectories);
//启动线程以创建所有图像的灰度缩略图
this.thumbnailThread=新线程(this.thumbnailThreadFunc);
this.thumbnailThread.Priority=ThreadPriority.lower;
this.thumbnailThread.Start(图像文件);
//设置一个计时器来启动我们。。。
this.startTimer=新计时器();
this.startTimer.Interval=500;
this.startTimer.Tick+=this.startTimer\u Tick;
这个.startTimer.Start();
#如果使用\u标签\u控件
this.label.Location=this.labelRect.Location;
this.label.Size=this.labelRect.Size;
this.label.Text=“已加载:0”;
this.label.BorderStyle=BorderStyle.FixedSingle;
this.Controls.Add(this.label);
#恩迪夫
基础荷载(e);
}
void startTimer_Tick(对象发送方,事件参数e)
{
//停止计时
this.startTimer.Stop();
//在循环中更新自己
while(this.IsHandleCreated)
{
int NextTick=Environment.TickCount+50;
//更新标签位置
this.labelRect.Offset(this.currentLabelDirection,0);
如果(this.labelRect.Right==this.ClientRectangle.Right||
this.labelRect.Left==0)
{
this.currentLabelDirection=-this.currentLabelDirection;
}
//更新显示
#如果使用\u标签\u控件
this.label.Text=“加载:”+this.thumbs.Count;
this.label.Location=this.labelRect.Location;
#否则
使用(Graphics Dest=this.CreateGraphics())
{
重绘控件(Dest,this.ClientRectangle);
}
#恩迪夫
Application.DoEvents();
Sleep(Math.Max(0,NextTick-Environment.TickCount));
}
}
私有void thumbnailThreadFunc(对象ThreadData)
{
字符串[]图像文件=(字符串[])线程数据;
foreach(ImageFiles中的字符串ImageFile)
{
如果(!this.IsHandleCreated)
{
返回;
}
使用(Image SrcImg=Image.FromFile(ImageFile))
{
矩形SrcRect=新矩形(Point.Empty,SrcImg.Size);
矩形DstRect=新矩形(Point.Empty,新大小(300200));
位图DstImg=新位图(DstRect.Width、DstRect.Height);
使用(Graphics Dst=Graphics.FromImage(DstImg))
{
使用(ImageAttributes Attrib=new ImageAttributes())
{
Attrib.SetColorMatrix(this.grayScaleMatrix);
Dst.DrawImage(SrcImg,
DstRect,
0,0,srrect.Width,srrect.Height,
像素,
Attrib);
}
}
锁(这个。拇指)
{
this.thumbs.Add(DstImg);
}
}
}
}
#如果!使用\u标签\u控件
专用空心重绘控件(图形目标、矩形)
{
位图OffscreenImg=新位图(this.ClientRectangle.Width,
此参数为0.ClientRectangle.Height);
使用(图形屏幕外=图形.FromImage(屏幕外))
{
屏幕外.FillRectangle(画笔.White,this.ClientRectangle);
屏幕外.DrawRectangle(Pens.Black,this.labelRect);
屏幕外。抽绳(“已加载:”+this.thumbs.Count,
SystemFonts.MenuFont,
刷子,黑色,