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

时间:2014-10-24 12:53:34

标签: c# winforms user-interface freeze

我在.NET 2.0上开发了一个带有C#的Winform UI(编辑:我可以将其迁移到3.5但不确定......因为服务器环境><)。 长期操作受到backgroundworker的限制,并且它会定期提供信息,以便通过事件" ProgressChanged"在UI中显示。到目前为止,非常好。

我的问题如下:长期操作可能是无限的(它在套接字上发送信息),因此用户必须通过"取消按钮"来停止它。唉,用户界面变得反应迟钝,有点冷冻,就像经过一个多小时的运行一样。

我试图设置一个计时器,每5分钟强制刷新一次UI,但这似乎没有帮助......

我的主要"顺便说一下,class有[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();
    }

1 个答案:

答案 0 :(得分:0)

似乎@groverboy之前发表的评论是一个简单的解决方案:我经常用一些信息更新“richTextBox”(充当“即时日志”)......太遗憾了。

频率更新不仅仅是添加字符串的绝对数量。虽然如果我从许多套接字开始,更新频率也可能成为一个问题。

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

感谢您的帮助!


编辑 -
我将解释我的问题来自哪里:最初我的软件可以测试特定IP上的套接字连接:端口。给定IP的接收服务应该处理多个连接。对于一个连接,您可以有多个发送(对于一个发送,多个消息)。对于每次发送,我都会在日志中写入,即GUI中的“richTextBox”:“informer n°X正确发送Y”。
这种情况没问题......只要定义了每个连接的连接数和发送数。该软件的发展是为了能够保持连接,但发送的数量无限。

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