C# 监视队列的线程<;行动>;

C# 监视队列的线程<;行动>;,c#,multithreading,snmp,C#,Multithreading,Snmp,我正在做一个使用SNMP映射网络(仅限路由器)的小项目。为了加快速度,除了第一个由主线程完成的任务外,我还尝试拥有一个线程池来负责完成我需要的任务 此时我有两个工作,一个接受参数,另一个不接受: UpdateDeviceInfo(网络设备nd) UpdateLinks()*尚未定义 我试图实现的是让那些工作线程等待作业完成 出现在队列上并在队列为空时等待。主线程将添加第一个作业,然后等待可能添加更多作业的所有工作线程完成,然后再开始添加第二个作业并唤醒休眠线程 我的问题是: 如何定义队列,

我正在做一个使用SNMP映射网络(仅限路由器)的小项目。为了加快速度,除了第一个由主线程完成的任务外,我还尝试拥有一个线程池来负责完成我需要的任务

此时我有两个工作,一个接受参数,另一个不接受:

  • UpdateDeviceInfo(网络设备nd)
  • UpdateLinks()
    *尚未定义
我试图实现的是让那些工作线程等待作业完成 出现在
队列上
并在队列为空时等待。主线程将添加第一个作业,然后等待可能添加更多作业的所有工作线程完成,然后再开始添加第二个作业并唤醒休眠线程

我的问题是:

  • 如何定义
    队列
    ,以便插入方法和参数(如果有)。如果不可能,我可以让所有函数都接受相同的参数

  • 如何无限期地启动工作线程。我不确定应该在哪里为(;;)创建

这是我目前的代码:

public enum DatabaseState
{
    Empty = 0,
    Learning = 1,
    Updating = 2,
    Stable = 3,
    Exiting = 4
};

public class NetworkDB
{
    public Dictionary<string, NetworkDevice> database;
    private Queue<Action<NetworkDevice>> jobs;
    private string _community;
    private string _ipaddress;

    private Object _statelock = new Object();
    private DatabaseState _state = DatabaseState.Empty;

    private readonly int workers = 4;
    private Object _threadswaitinglock = new Object();
    private int _threadswaiting = 0;

    public Dictionary<string, NetworkDevice> Database { get => database; set => database = value; }

    public NetworkDB(string community, string ipaddress)
    {
        _community = community;
        _ipaddress = ipaddress;
        database = new Dictionary<string, NetworkDevice>();
        jobs = new Queue<Action<NetworkDevice>>();
    }

    public void Start()
    {
        NetworkDevice nd = SNMP.GetDeviceInfo(new IpAddress(_ipaddress), _community);
        if (nd.Status > NetworkDeviceStatus.Unknown)
        {
            database.Add(nd.Id, nd);
            _state = DatabaseState.Learning;
            nd.Update(this); // The first job is done by the main thread 

            for (int i = 0; i < workers; i++)
            {
                Thread t = new Thread(JobRemove);
                t.Start();
            }

            lock (_statelock)
            {
                if (_state == DatabaseState.Learning)
                {
                    Monitor.Wait(_statelock);
                }
            }

            lock (_statelock)
            {
                if (_state == DatabaseState.Updating)
                {
                    Monitor.Wait(_statelock);
                }
            }

            foreach (KeyValuePair<string, NetworkDevice> n in database)
            {
                using (System.IO.StreamWriter file = new System.IO.StreamWriter(n.Value.Name + ".txt")
                {
                    file.WriteLine(n);

                }
            }
        }
    }

    public void JobInsert(Action<NetworkDevice> func, NetworkDevice nd)
    {
        lock (jobs)
        {
            jobs.Enqueue(item);
            if (jobs.Count == 1)
            {
                // wake up any blocked dequeue
                Monitor.Pulse(jobs);
            }
        }
    }

    public void JobRemove()
    {
        Action<NetworkDevice> item;
        lock (jobs)
        {
            while (jobs.Count == 0)
            {
                lock (_threadswaitinglock)
                {
                    _threadswaiting += 1;
                    if (_threadswaiting == workers)
                        Monitor.Pulse(_statelock);
                }
                Monitor.Wait(jobs);
            }

            lock (_threadswaitinglock)
            {
                _threadswaiting -= 1;
            }

            item = jobs.Dequeue();
            item.Invoke();
        }
    }

    public bool NetworkDeviceExists(NetworkDevice nd)
    {
        try
        {
            Monitor.Enter(database);
            if (database.ContainsKey(nd.Id))
            {
                return true;
            }
            else
            {
                database.Add(nd.Id, nd);
                Action<NetworkDevice> action = new Action<NetworkDevice>(UpdateDeviceInfo);
                jobs.Enqueue(action);
                return false;
            }
        }
        finally
        {

            Monitor.Exit(database);
        }
    }

    //Job1 - Learning -> Update device info
    public void UpdateDeviceInfo(NetworkDevice nd)
    {
        nd.Update(this);
        try
        {
            Monitor.Enter(database);
            nd.Status = NetworkDeviceStatus.Self;
        }
        finally
        {
            Monitor.Exit(database);
        }
    }

    //Job2 - Updating -> After Learning, create links between neighbours
    private void UpdateLinks()
    {

    }
}
公共枚举数据库状态
{
空=0,
学习=1,
更新=2,
稳定=3,
退出=4
};
公共类网络数据库
{
公共词典数据库;
专用队列作业;
私人字符串(u)社区;;
私有字符串\u ipaddress;
私有对象_statelock=新对象();
私有数据库状态_state=DatabaseState.Empty;
私有只读int-workers=4;
私有对象_threadswaitinglock=新对象();
私有int_threadswaiting=0;
公共字典数据库{get=>Database;set=>Database=value;}
公共网络数据库(字符串社区、字符串IP地址)
{
_社区=社区;
_IP地址=IP地址;
数据库=新字典();
作业=新队列();
}
公开作废开始()
{
NetworkDevice nd=SNMP.GetDeviceInfo(新IpAddress(_IpAddress),_community);
如果(nd.Status>网络设备状态未知)
{
添加(nd.Id,nd);
_状态=数据库状态。学习;
Update(this);//第一个作业由主线程完成
for(int i=0;i更新设备信息
public void UpdateDeviceInfo(网络设备nd)
{
更新(本);
尝试
{
进入(数据库);
nd.Status=NetworkDeviceStatus.Self;
}
最后
{
监控退出(数据库);
}
}
//作业2-更新->学习后,在邻居之间创建链接
私有void UpdateLinks()
{
}
}

您的最佳选择似乎是使用BlockingCollection而不是Queue类。它们在FIFO方面的行为实际上是相同的,但BlockingCollection会让您的每个线程阻塞,直到可以通过调用GetConsumingEnumerable或Take获取某个项目。下面是一个完整的示例


至于包含参数,您似乎可以使用closure来封装网络设备本身,然后将操作而不是操作排入队列

一个
阻止集合
是否符合您的需要?不同的
数据库状态
的确切含义是什么?数据库状态的可能副本是当前状态。如果无法访问第一个设备。学习时正在查找每个设备路由表并为每个邻居跳转。更新后,用于创建设备之间的链接。我选择为此创建状态,因为只有在了解所有关于邻居的信息后,我才知道哪个neigbhour接口用于与当前设备的链接。退出ng是我给ev发信号的地方