C#列表并发 [英] C# list concurrency

查看:138
本文介绍了C#列表并发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用一个简单的irc聊天机器人(专门用于twitch.tv流),我使用列表来保存频道中的所有用户的列表。当有人离开或加入时,我会从列表中添加或删除它们。然后我有一个线程,每分钟运行,检查流是否在线,如果是,它给我的用户列表中的所有人货币。



我相信你可以看到我的问题在哪里。如果有人离开或加入,而我的程序正在循环通过我的列表中的用户,那么我得到一个收集修改异常。目前,作为一个解决方法,我只是做一个临时列表,并将真实的列表复制到它,然后循环通过临时列表,但我只是好奇,如果有一个更好的方式做到这一点?



快速验证码:

  private List< string& users = new List< string>(); 

private void IrcInitialize(){
//连接到irc东西
// blah
// blah
// blah
workThread = new Thread(new ThreadStart(doWork());
workThread.Start();
}

private void ircListener(){
parseIRCMessage(StreamReader。 ReadLine());
}

private void parseIRCMessage(msg){
if(msgType ==JOIN){
users.Add(user);
}
else if(msgType ==PART){
users.Remove(user);
}
}

private void doWork(){
while(true){
if(streamOnline()&&& handOutTime()){
handOutCurrency();
}
.Sleep(60000);
}
}

private void handOutCurrency(){
List< string> temp = users; //这是我目前正在做
foreach(String user in temp){
database.AddCurrency(user,1);
}
}
pre>

任何其他建议?

解决方案

解决此问题:




  • 使用锁定来同步两个主题之间的访问,或



  • 第一种方法很简单:add lock(users){... } c>



    第二个方式稍微涉及:定义两个并发队列 toAdd toRemove 。而不是直接从 users 列表添加或删除用户,将它们添加到 toAdd toRemove 队列。当睡眠线程被唤醒时,它应该首先清空两个队列,根据需要执行修改。只有这样才能分发货币。

      ConcurrentQueue< string> toAdd = new ConcurrentQueue< string>(); 
    ConcurrentQueue< string> toRemove = new ConcurrentQueue< string>();

    private void parseIRCMessage(msg){
    if(msgType ==JOIN){
    toAdd.Enqueue(user);
    }
    else if(msgType ==PART){
    toRemove.Enqueue(user);
    }
    }
    private void doWork(){
    while(true){
    string user;
    while(toAdd.TryDequeue(out user)){
    users.Add(user);
    }
    while(toRemove.TryDequeue(out user)){
    users.Remove(user);
    }
    if(streamOnline()&&& handOutTime()){
    handOutCurrency();
    }
    Thread.Sleep(60000);
    }
    }


    I'm working on a simple irc chat bot (specifically for twitch.tv streams), and I'm using a List to keep a list of all the users in the channel. When someone leaves or joins, I add or remove them from the list. Then I have a thread that runs every minute that checks if the stream is online, and if it is, it hands out "currency" to all the people in my user list.

    I'm sure you can already see where my problem is. If someone leaves or joins while my program is looping through the users in my list, then I get a Collection Modified exception. Currently, as a workaround, I just make a temp list and copy the real list into it, then loop through the temp list instead, but I was just curious if there was a "better" way to do it?

    Quick psuedocode:

    private List<string> users = new List<string>();
    
    private void IrcInitialize(){
        //connect to irc stuff
        //blah
        //blah
        //blah
        Thread workThread = new Thread(new ThreadStart(doWork());
        workThread.Start();
    }
    
    private void ircListener(){
        parseIRCMessage(StreamReader.ReadLine());
    }
    
    private void parseIRCMessage(msg){
        if (msgType == "JOIN"){
            users.Add(user);
        }
        else if (msgType == "PART"){
            users.Remove(user);
        }
    }
    
    private void doWork(){
        while (true) {
            if (streamOnline() && handOutTime()){
                handOutCurrency();
            }
            Thread.Sleep(60000);
        }
    }
    
    private void handOutCurrency(){
        List<string> temp = users; //This is what I'm currently doing
        foreach (String user in temp) {
            database.AddCurrency(user, 1);
        }
    }
    

    Any other suggestions?

    解决方案

    There are two ways to solve this problem:

    • Using a lock to synchronize access between two threads, or
    • Doing all access from a single thread.

    The first way is simple: add lock(users) {...} block around the code that reads or modifies the users list.

    The second way is slightly more involved: define two concurrent queues, toAdd and toRemove in your class. Instead of adding or removing users directly from the users list, add them to the toAdd and toRemove queues. When the sleeping thread wakes up, it should first empty both queues, performing the modifications as necessary. Only then it should hand out the currency.

    ConcurrentQueue<string> toAdd = new ConcurrentQueue<string>();
    ConcurrentQueue<string> toRemove = new ConcurrentQueue<string>();
    
    private void parseIRCMessage(msg){
        if (msgType == "JOIN"){
            toAdd.Enqueue(user);
        }
        else if (msgType == "PART"){
            toRemove.Enqueue(user);
        }
    }
    private void doWork(){
        while (true) {
            string user;
            while (toAdd.TryDequeue(out user)) {
                users.Add(user);
            }
            while (toRemove.TryDequeue(out user)) {
                users.Remove(user);
            }
            if (streamOnline() && handOutTime()){
                handOutCurrency();
            }
            Thread.Sleep(60000);
        }
    }
    

    这篇关于C#列表并发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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