阻止GUI的网络线程 [英] Network threads blocking the GUI

查看:117
本文介绍了阻止GUI的网络线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我正在开发一个应用程序,用于处理局域网上的数据传输。



特别是,对于每个传输进度条,如果是下载,则显示绿色,如果是上传,则显示红色。

我做了一些虚拟的尝试,其中我模拟了传输(网络线程被禁用)。所以这些酒吧被编程填充,我检查了GUI的性能,一切都很好。特别是我可以选择条形图并出现上下文菜单。

过了一段时间,我们开发了基本上可以读写TCP的网络部分。对于每个块(或一组块),执行一个步骤以使该块前进。



块的数量因文件而异,但是步数如果有100个以上的组块,那么总是100个,这样每个转移总是有100个或更少的执行步骤。

问题是,当真实转移开始,一切都放慢了,当我拖动窗口四周,GUI不再响应时,我甚至无法点击栏以使上下文菜单出现。然而,酒吧的工作进展。



我们尝试禁用酒吧并进行传输,GUI响应并且没有问题。



可能我们会伤害网络上的GUI刷新。



网络有自己的线程,另一个是GUI。



从网络中刷新酒吧的最佳方法是什么?



我们现在使用这个,但是我们做不相信这是一种好方法,肯定它效率不高。

例如,我们采用网络客户端代码:

  ... 
//表示传输的对象(在GUI和网络之间共享)
传输t =新传输(...);

int bytesRead;
var buffer = new byte [chunkSize]; ((bytesRead = file.Read(buffer,0,buffer.Length))> 0)
{
if(t.Stop)
{
打破;
}

nwStream.Write(buffer,0,bytesRead);
PBcount ++;
if(PBcount == PBchuks)
{
t.PerformStep(); //使进度条前进,工作,但滞后
PBcount = 0;
}
}
...

与代码相关到PerformStep()是Transfer共享对象的公共方法:

  public void PerformStep()
{
CurrentStep = CurrentStep + 1;
Application.Current.Dispatcher.Invoke(delegate {
MainWindow wnd =(MainWindow)Application.Current.MainWindow;
wnd?.PerformStepProgressBarRefresh();
});



$ b

以下是委托刷新ListBox项目的MainWindow GUI方法的代码:

  public void PerformStepProgressBarRefresh()
{
Application.Current.Dispatcher.Invoke(delegate
{
TransfersXAML.Items.Refresh();
});
}

我不知道它是否有用,但我会说无论如何,这是与进度条相关的XAML代码:

 < ListBox x:Name =TransfersXAML
Horizo​​ntalContentAlignment =Stretch
ItemsSource ={Binding Transfers}>
< ListBox.ItemTemplate>
< DataTemplate>
< ProgressBar Height =30Minimum =0
Maximum ={Binding NSteps}
Value ={Binding CurrentStep}
Foreground ={Binding Color}/>
< / DataTemplate>
< /ListBox.ItemTemplate>
< ListBox.ContextMenu>
< ContextMenu>
< MenuItem Header =Get infoClick =GetTransferInfoClick/>
< MenuItem Header =CancelClick =CancelTransferClick/>
< / ContextMenu>
< /ListBox.ContextMenu>
< / ListBox>您知道一个更好,更高效的系统,以便网络的线程可以更新进度条一些方法?

毕竟,我们不知道我们是否做得正确。




我们可能没有提供所有有助于更好地理解问题的信息:发出信号,以便我们更新问题。


更新




 公共类转移
{
public enum Type_t
{
upload,
download
}

public int NSteps {get;组; }
public int CurrentStep {get;组; }
public Type_t Type {get;组; }
public string Color {get;组; }
公共字符串ID {get;组; }
public string SenderID {get;组; }
public string ReceiverID {get;组; }
$ b $ public Transfer(string senderID,string receiverID,Type_t _type,string _color)
{
SenderID = senderID;
ReceiverID = receiverID;
NSteps = 100;
CurrentStep = 0;
Type = _type;
Color = _color;
ID = Utils.GetHashString(senderID + receiverID + Utils.GetCurrentTimestamp());
}

public void PerformStep()
{
CurrentStep = CurrentStep + 1;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,new Action(()=>
{
MainWindow wnd =(MainWindow)Application.Current.MainWindow;
wnd ?.PerformStepProgressBarRefresh();
}));



$ div $解析方案

你应该不需要使用 Refresh 或类似技术手动刷新WPF控件。 您正确地将您的进度条属性绑定到 $ b> code> Transfer.CurrentStep ,但是您没有实现更改通知以自动通知绑定关于更改。为此,您需要在 Transfer 类上实现 INotifyPropertyChanged 接口,例如:

  public class Transfer:INotifyPropertyChanged {
private int _currentStep;
public int CurrentStep
{
get {return _currentStep; }
set
{
if(_currentStep!= value){
_currentStep = value;
OnPropertyChanged();
}
}
}

公共事件PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged([CallerMemberName] string propertyName = null){
PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));






$ p然后,删除所有 PerformStep () PerformStepProgressBarRefresh - 您不再需要它们,也不需要手动执行 Dispatcher.Invoke BeginInvoke 。在你的网络代码中:


  t.CurrentStep ++; 

就是这样。


I'm just working on C # and WPF.

I'm developing an application that should handle data transfers on the LAN.

In particular, for each transfer progress bars are displayed, green if it is a download, red if it is an upload.

Initially I had made some fictitious attempts in which I simulated transfers (the threads of the network were deactivated). So the bars were filled programmatically and I checked the performance of the GUI, everything was fine. In particular I could select the bars and make a context menu appear.

After some time, we have developed the part of the network which basically does Read and Write TCP. For each chunk (or group of chunks) a Perform Step is made so that the bar advances.

The number of chunks varies from file to file, but the number of steps to do is always 100 if there are more than 100 chunks, in this way 100 or less Perform Steps are always made for each transfer.

The problem is that when the real transfers start, everything is slowed down, when I drag the window all around and the GUI is no longer responsive I can not even click on the bars to make the context menu appear. However, the progress of the bars works.

We tried to disable the bars and make the transfers, the GUI is responsive and there are no problems.

Probably we hurt the GUI refresh from the network.

The Network has its own thread and the GUI another.

What is the best way to refresh the bars by doing it from the Network?

We currently use this, but we do not believe it is a good way, for sure it is not efficient.

For example, we take the Network Client code:

...
// Object representing a transfer (shared between GUI and Network)
Transfer t = new Transfer(...);

int bytesRead;
var buffer = new byte[chunkSize];
while ((bytesRead = file.Read(buffer, 0, buffer.Length)) > 0)
{
    if (t.Stop)
    {
        break;
    }

    nwStream.Write(buffer, 0, bytesRead);
    PBcount++;
    if (PBcount == PBchuks)
    {
        t.PerformStep(); // Make the progress bar to advance, works, but laggy
        PBcount = 0;
    }
}
...

And the code relative to the PerformStep() is a public method of the Transfer shared object:

public void PerformStep()
{
    CurrentStep = CurrentStep + 1;
    Application.Current.Dispatcher.Invoke(delegate { 
        MainWindow wnd = (MainWindow)Application.Current.MainWindow;
        wnd?.PerformStepProgressBarRefresh();
    });
}

Following is the code of the MainWindow GUI method delegated to refresh the ListBox items:

public void PerformStepProgressBarRefresh()
{
    Application.Current.Dispatcher.Invoke(delegate
    {
        TransfersXAML.Items.Refresh();
    });
}

I do not know if it can be useful, but I'll put it anyway, it's the XAML code related to the progress bars:

<ListBox x:Name="TransfersXAML"
         HorizontalContentAlignment="Stretch"
         ItemsSource="{Binding Transfers}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <ProgressBar Height="30" Minimum="0"
                         Maximum="{Binding NSteps}"
                         Value="{Binding CurrentStep}"
                         Foreground="{Binding Color}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
    <ListBox.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Get info" Click="GetTransferInfoClick" />
            <MenuItem Header="Cancel" Click="CancelTransferClick" />
        </ContextMenu>
    </ListBox.ContextMenu>
</ListBox>

Do you know a better and more efficient system so that the threads of the Network can update progress bars in some way?

After all, we do not know if we have done things correctly.


We probably did not put all the information that could be useful to better understand the problem: signal it so that we can update the question.

Update

public class Transfer
{
    public enum Type_t
    {
        upload,
        download
    }

    public int NSteps { get; set; }
    public int CurrentStep { get; set; }
    public Type_t Type { get; set; }
    public string Color { get; set; }
    public string ID { get; set; }
    public string SenderID { get; set; }
    public string ReceiverID { get; set; }

    public Transfer(string senderID, string receiverID, Type_t _type, string _color)
    {
        SenderID = senderID;
        ReceiverID = receiverID;
        NSteps = 100;
        CurrentStep = 0;
        Type = _type;
        Color = _color;
        ID = Utils.GetHashString(senderID + receiverID + Utils.GetCurrentTimestamp());
    }

    public void PerformStep()
    {
        CurrentStep = CurrentStep + 1;
   Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
        {
            MainWindow wnd = (MainWindow)Application.Current.MainWindow;
            wnd?.PerformStepProgressBarRefresh();
        }));
    }
}

解决方案

You should not need to ever refresh WPF controls manually with Refresh or similar techniques. And you certainly don't need to do it here.

You correctly bound your progress bar Value property to Transfer.CurrentStep, but you didn't implement change notification to automatically notify binding about changes. For that you need to implement INotifyPropertyChanged interface on Transfer class, for example:

public class Transfer : INotifyPropertyChanged {
    private int _currentStep;
    public int CurrentStep
    {
        get { return _currentStep; }
        set
        {
            if (_currentStep != value) {
                _currentStep = value;
                OnPropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Then, remove all PerformStep() and PerformStepProgressBarRefresh - you don't need them any more, nor do you need to manually do Dispatcher.Invoke or BeginInvoke. In your network code do:

t.CurrentStep++;

That's all.

这篇关于阻止GUI的网络线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆