【问题标题】:Class member behave differently when define as static or non static定义为静态或非静态时,类成员的行为不同
【发布时间】:2015-09-18 18:08:47
【问题描述】:

我有带有PcapDotNet DLL 的WPF 应用程序可以测量我的机器Interface Rate

这是Model

public class Interface
{
    public PacketDevice PacketDevice { get { return livePacketDevice; } }
    private DateTime _lastTimestamp;
    private double _bitsPerSecond;
    private double _packetsPerSecond;
    private DateTime _lastTimestamp;
    private static List<Interface> _machineInterfaces; // list of all machine interfaces

    public void Start(Interface inf)
    {
        OpenAdapterForStatistics(inf.PacketDevice);
    }

    public void OpenAdapterForStatistics(PacketDevice selectedOutputDevice)
    {
        if (selectedOutputDevice != null)
        {
            using (PacketCommunicator statCommunicator = selectedOutputDevice.Open(100, PacketDeviceOpenAttributes.Promiscuous, 1000)) //open the output adapter
            {
                try
                {
                    statCommunicator.Mode = PacketCommunicatorMode.Statistics; //put the interface in statstics mode
                    statCommunicator.ReceiveStatistics(0, StatisticsHandler); //start the main loop
                }
                catch (Exception)
                { }
            }
        }
    }

    private void StatisticsHandler(PacketSampleStatistics statistics)
    {
        DateTime currentTimestamp = statistics.Timestamp; //current sample time
        DateTime previousTimestamp = _lastTimestamp; //previous sample time
        _lastTimestamp = currentTimestamp; //set _lastTimestamp for the next iteration

        if (previousTimestamp == DateTime.MinValue) //if there wasn't a previous sample than skip this iteration (it's the first iteration)
            return;

        double delayInSeconds = (currentTimestamp - previousTimestamp).TotalSeconds; //calculate the delay from the last sample
        _bitsPerSecond = statistics.AcceptedBytes * 8 / delayInSeconds; //calculate bits per second
        _packetsPerSecond = statistics.AcceptedPackets / delayInSeconds; //calculate packets per second

        if (NewPointEventHandler != null)
            NewPointEventHandler(_bitsPerSecond);
        double value = _packetsPerSecond;
    }

如您所见,Start 方法开始测量 Interface 速率并将值放入 2 个字段中:

_bitsPerSecond_packetsPerSecond

所以在应用程序启动后我有这个字段:

List<Interface> _machineInterfaces; 

这读取了我所有的机器接口。

之后我开始我的Start 方法:

    private void StartStatistics()
    {
        int index = listview.SelectedIndex; // select the selected interface from my `ListView` list.
        Interface inf = new Interface();
        ThreadStart tStarter = delegate
        {              
            inf.Start(Interface.MachineInterfaces[index]); // send the selected interface
        };
        Thread thread = new Thread(tStarter);
        thread.IsBackground = true;
        thread.Start();

        statisticsTimer.Start(); // start my timer
    }
  • 好的,现在这是我的问题:

这是我的Timer Tick Event

public RadObservableCollection<double> mbitPerSecondValue { get; private set; }

如果我的 BitsPerSecond Class Interface member 定义为常规而不是 Static 它的值始终为零:

    private void statisticsTimer_Tick(object sender, EventArgs e)
    {
        int index = listview.SelectedIndex;

        double bps = Interface.MachineInterfaces[index].BitsPerSecond; // always zero !
        mbitPerSecondValue.Add(bps);
    }

如果BitsPerSecond 定义为静态一切都好:

    private void statisticsTimer_Tick(object sender, EventArgs e)
    {
        int index = listview.SelectedIndex;
        double bps = Interface.BitsPerSecond;
        mbitPerSecondValue.Add(bps);
    }

所以我的问题是为什么?

编辑

目前我改变了我的功能:

private void StartStatistics()
        {
            int index = lvAdapters.SelectedIndex;
            Interface inf = new Interface();
            ThreadStart tStarter = delegate
            {
                foreach (Interface item in Interface.MachineInterfaces)
                    item.Start();
            };

            Thread thread = new Thread(tStarter);
            thread.IsBackground = true;
            thread.Start();

            statisticsTimer.Start();
        }

我想要实现的是在我的机器上打开每个接口的统计信息,但是在第一个接口中(我有 2 个)我可以看到流量在变化(BitsPerSecond)但在第二个接口中它总是为零(我让一定会通过这个接口产生一些流量,所以它不应该是零)

【问题讨论】:

  • 您的编辑是一个不同的问题,也许应该这样做,因为它不再与类成员是否为静态有关(这就是问题标题的含义)。
  • 另外...我看不出有什么奇怪的地方。您是否调试过您的应用程序并确保正在调用 StatisticsHandler 并实际更新第二个接口中的值?也许它是零,即使它不应该是。
  • 你是说我的代码现在打开了我所有的界面,这看起来不错?

标签: wpf multithreading pcap.net


【解决方案1】:

嗯,很明显为什么它在定义为 static 时起作用:Interface 的所有实例都共享相同的属性,因此当您从一个位置增加它的值时,新值会自动在任何地方可用。

但作为常规的非静态属性,您必须确保从之前修改过的同一个实例中读取。而你不是。

首先,您要创建一个新的Interface(我们称之为接口A),然后调用它的Start,并传递您从@ 获得的另一个Interface(我们称之为接口B) 987654326@,作为参数:

private void StartStatistics()
{
    ...
    Interface inf = new Interface();
    ThreadStart tStarter = delegate
    {              
        inf.Start(Interface.MachineInterfaces[index]); // send the selected interface
    };
    ...
}

在接口A的Start方法中,订阅了接口B的统计信息,但是handler还在接口A中:

public void Start(Interface inf)
{
    OpenAdapterForStatistics(inf.PacketDevice);
}

public void OpenAdapterForStatistics(PacketDevice selectedOutputDevice)
{
    ...
    statCommunicator.ReceiveStatistics(0, StatisticsHandler); //start the main loop
    ...
}

当调用接口 A 中的处理程序时,它会增加自己的 _bitsPerSecond 值。不是接口 B,而是接口 A。

private void StatisticsHandler(PacketSampleStatistics statistics)
{
    ...
    _bitsPerSecond = statistics.AcceptedBytes * 8 / delayInSeconds; //calculate bits per second
    ...
}

但最后,您正在检查接口 B 中 BitsPerSecond 的值,再次取自 Interface.MachineInterfaces

private void statisticsTimer_Tick(object sender, EventArgs e)
{
    ...
    double bps = Interface.MachineInterfaces[index].BitsPerSecond; // always zero !
    ...
}

-- 建议的解决方案 1 --

您为什么不直接让Start 使用它自己的实例,这样您就不必为了使用它而创建一个新的Interface

public void Start()
{
    OpenAdapterForStatistics(this.PacketDevice);
}

这样你就可以做到:

private void StartStatistics()
{
    int index = listview.SelectedIndex; // select the selected interface from my `ListView` list.
    ThreadStart tStarter = delegate
    {              
        Interface.MachineInterfaces[index].Start(); // send the selected interface
    };
    Thread thread = new Thread(tStarter);
    thread.IsBackground = true;
    thread.Start();

    statisticsTimer.Start(); // start my timer
}

...您应该在 Timer Tick 回调中获得所需的输出。

-- 建议的解决方案 2--

如果您不想从Interface.MachineInterfaces 中的原始接口调用Start,那么您必须将新接口存储在某种字典中,以便稍后访问它以获取BitsPerSecond来自它:

private Dictionary<Interface, Interface> InterfaceDictionary = new Dictionary<Interface, Interface>();

private void StartStatistics()
{
    int index = listview.SelectedIndex; // select the selected interface from my `ListView` list.
    Interface inf = new Interface();
    ThreadStart tStarter = delegate
    {              
        inf.Start(Interface.MachineInterfaces[index]); // send the selected interface
    };
    Thread thread = new Thread(tStarter);
    thread.IsBackground = true;
    thread.Start();

    statisticsTimer.Start(); // start my timer

    if (InterfaceDictionary.ContainsKey(Interface.MachineInterfaces[index]))
        InterfaceDictionary[Interface.MachineInterfaces[index]] = inf;
    else
        InterfaceDictionary.Add(Interface.MachineInterfaces[index], inf);
}

在您的 Timer Tick 回调中,从关联的接口中获取数据,而不是从 Interface.MachineInterfaces 中的接口获取数据:

private void statisticsTimer_Tick(object sender, EventArgs e)
{
    int index = listview.SelectedIndex;
    var interface = InterfaceDictionary[Interface.MachineInterfaces[index]];
    double bps = interface.BitsPerSecond;
    mbitPerSecondValue.Add(bps);
}

【讨论】:

  • 所以应该在我的 Interface.MachineInterfaces 中的每个接口上调用 Start() 方法?
  • 不一定...我想说的是,从一个接口调用Start,然后从另一个接口检查BitsPerSecond 是行不通的。您需要两个对象相同,因此您可以从 MachineInterfaces 中的原始接口对象调用Start,或者存储为调用Start 而创建的新接口,以便稍后检查它的BitsPerSecond(我'也会将该选项添加到我的答案中)。
  • 我只想用我所有的机器接口调用 Start() 来同时在我的图表中显示它的所有值,那么最好的方法是什么?
  • 这不是你的代码在做什么,也不是问题是关于......对吗? :/ 现在看来您只是为选定的接口调用Start(通过某些 ListView 的 SelectedIndex),并且您问为什么它在属性为静态时起作用。
  • 那是因为这不起作用所以我将其更改为有效的东西,我希望在我的 StartStatistics() 函数中打开我所有机器接口的统计信息并同时显示它的所有值
【解决方案2】:

对于第二个问题,尝试从不同的线程调用每个接口的Start。我看到的唯一可疑的事情是 statCommunicator.ReceiveStatistics 可能正在阻塞线程并阻止其他接口被启动。

这应该可以避免这个问题:

private void StartStatistics()
{
    foreach (Interface item in Interface.MachineInterfaces)
    {
        ThreadStart tStarter = delegate
        {
            item.Start();
        };

        Thread thread = new Thread(tStarter);
        thread.IsBackground = true;
        thread.Start();
    }

    statisticsTimer.Start();
}

【讨论】:

    猜你喜欢
    • 2012-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-17
    • 2012-03-13
    • 1970-01-01
    相关资源
    最近更新 更多