繁体   English   中英

C#WinForm应用程序UI长时间冻结

[英]C# WinForm application UI freezes after long period

我已经在.NET 2.0上使用C#开发了Winform UI(编辑:我可以将其迁移到3.5,但不确定...因为服务器环境> <)。
长期操作应由后台工作人员进行适当包装,并且它定期通过事件“ ProgressChanged”将信息返回给用户,以在UI中显示。 到现在为止还挺好。

我的问题如下:长期操作可能是无限的(它在套接字上发送信息),因此用户必须通过“取消按钮”将其停止。 遗憾的是,UI变得无响应,有点死机,就像经过1个小时的运行之后。

我尝试设置一个计时器,以强制每5分钟刷新一次UI,但这似乎无济于事。

我的“主要”类具有[STAThread]属性。

我该如何解决?

编辑-WinForm中的代码如下所示:

public MainForm()
{
    InitializeComponent();

    this._bw = new BackgroundWorker();
    this._bw.WorkerReportsProgress = true;
    this._bw.WorkerSupportsCancellation = true;
    this._bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
    this._bw.ProgressChanged += new ProgressChangedEventHandler(_bw_ProgressChanged);
    this._bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_RunWorkerCompleted);
    //... more initialization
}

private void btn_sendMessagesContinuous_Click(object sender, EventArgs e)
{
    //Argument construction
    BackgroundWorkerArgument bwArgument = this.CheckParametersAndCreateBwArgument(BackgroundWorkerTaskEnum.ConnectionContinuous);

    //Launch
    if (this._bw.IsBusy == false)
    {
        //Activating ProgressBar and CancelButton
        this.SetControlsBeforeWork();
        this._bw.RunWorkerAsync(bwArgument);
    }
}

private void _bw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    RmessService rmessService = null;

    //Timer for GUI refresh
    _refreshTimer.Start();

    //Getting the arguments
    BackgroundWorkerArgument bwArgument = e.Argument as BackgroundWorkerArgument;
    BackgroundWorkerTaskEnum bwTask = bwArgument.BackgroundWorkerTaskEnum;

    try
    {
        switch (bwTask)
        {
            case BackgroundWorkerTaskEnum.ConnectionContinuous:

                rmessService = new RmessService(worker);
                bwArgument.ServiceMakesReports = true;

                rmessService.ConnectToRmess(bwArgument);

                while (worker.CancellationPending == false && rmessService.IsStillConnected())
                { 
                        // Used to lower the processor usage
                        System.Threading.Thread.Sleep(100); 
                }

                break;

            case ...

        }
    }
    catch (Exception ex)
    { //... do the log and stuff ... }
    finally
    {
       if (rmessService != null)
       { rmessService.Dispose(); }
    }

现在,对于服务,我选择将backgroundWorker传递到服务的构造函数中。 我知道,应该已经经历了服务内部的事件,但这不是重点……希望吗?

因此,我在服务中有以下内容:

    ///Constructor 
    public RmessService(BackgroundWorker backgroundWorker)
    {
        this._bwMainForm = backgroundWorker;
        this._dicoThreadSocketConnection = new Dictionary<BackgroundWorker, bool>();
    }

    ///Main method of the service
    public void ConnectToRmess(BackgroundWorkerArgument bwArguments)
    {
        this._bwMainForm.ReportProgress(0, new BackgroundWorkerProgressState(BackgroundWorkerProgressEnum.SetProgressInformation, "Test de connexion au socket en cours ..."));

        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        try
        {
            socket.Connect(bwArguments.AdresseIP, bwArguments.Port);
            Thread.Sleep(100);
        }
        finally
        {
            if (socket.Connected)
            { socket.Disconnect(false); }
        }

        this._bwMainForm.ReportProgress(0, new BackgroundWorkerProgressState(BackgroundWorkerProgressEnum.SetProgressInformation, "Envoi des messages en cours ..."));
        int index = 0;
        long idBoitier = bwArguments.IdBoitier;
        while (index < bwArguments.NombreConnexions)
        {
            BackgroundWorker bwSocket = new BackgroundWorker();
            bwSocket.WorkerReportsProgress = true;
            bwSocket.WorkerSupportsCancellation = true;
            bwSocket.DoWork += new DoWorkEventHandler(bwSocket_DoWork);
            bwSocket.ProgressChanged += new ProgressChangedEventHandler(bwSocket_ProgressChanged);
            bwSocket.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwSocket_RunWorkerCompleted);

            //Ajout du BW à la liste
            this._dicoThreadSocketConnection.Add(bwSocket, false);

            //Construction des arguments à passer
            List<object> listArguments = new List<object>() { bwArguments, idBoitier };
            bwSocket.RunWorkerAsync(listArguments);
            Thread.Sleep(100);

            //Incrémentation
            idBoitier++;
            index++;
        }
    }

    private void bwSocket_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        //Simple message transfer
        this._bwMainForm.ReportProgress(0, e.UserState);
    }

编辑2:这是其余的代码。

    private void _bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        BackgroundWorkerProgressState bwProgressState = e.UserState as BackgroundWorkerProgressState;

        switch (bwProgressState.BackgroundWorkerProgressEnum)
        {
            case BackgroundWorkerProgressEnum.SetProgressInformation:
                this.lbl_progressInfo.Text = bwProgressState.State as string;
                break;

            case BackgroundWorkerProgressEnum.AddSocketProgressMessage:
                this.richTextBox_generalInfo.Text += (bwProgressState.State as string) + "\r\n";
                break;

            default:
                break;
        }
    }

    private void _bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //Stopping ProgressBar animation
        this.SetControlsAfterWork();
    }

好像@groverboy先前的评论是一种简单的解决方案: 不幸的是,我经常用一些信息来更新“ richTextBox”(充当“即时日志”)……

频率更新所带来的问题要比所添加字符串的绝对数量少。 尽管如果我从许多套接字开始,更新频率也可能会成为问题。

我用许多可能的方法之一解决了它:更新较少的信息。 一句话记录的时间为一分钟,而不是每分钟1000个。
当然,保留日志的最佳方法是将其放入文件中。 就我而言,日志没有其他目的,只能立即说“一切都很好”。 另一种方法是剔除richTextBox中的文本。

感谢您的帮助!


编辑-
我将说明问题的出处:最初,我的软件可以测试特定IP:端口上的套接字连接。 给定IP上的接收服务应该处理多个连接。 对于一个连接,您可以有多个发送(对于一个发送,则可以有多个消息)。 对于每次发送,我都将在日志中写入日志,即GUI中的“ richTextBox”:“通知人X正确发送Y”。
只要定义了您的连接号和每个连接的发送,这种情况就没问题了。 该软件经过改进,可以保持连接,但发送数量不受限制。

在后一种情况下,即使连接数很小,文本也会增长得太快(一个连接大约发送10条消息,因此只有10条消息)。
当长度超过10.000个字符时,我已经在TextChanged事件中测试了文本重置:使用使我的GUI冻结的相同设置完全没有问题。
这使我认为字符串长度是主要问题,尽管更新频率也可能使情况更糟。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM