对 ObservableCollection 更新进行排队
我正在编写一个 TAPI 应用程序,它使用状态模式来处理 TK 可能处于的不同状态。传入和传出调用通过 ListView
中的 ObservableCollection
进行记录(打电话给日记)。呼叫数据与存储在 SQL-Server 数据库中的联系人进行比较,以确定可能的匹配项。然后,该信息将用于更新呼叫日志。当然,所有这些都是实时的,并且全部由 FSM(有限状态机)的不同状态控制。
为了区分呼叫,我使用了呼叫 ID(由 TAPI 提供)。当电话响起或我开始呼叫时,包括其呼叫 ID 在内的新记录将添加到呼叫日志中,并在客户数据库中搜索该号码,并相应地更新日志中的某些数据。当处理不同的呼叫状态时,应用程序会动态更新日志(即更改直观地显示特定呼叫状态的图标等)。
正是这些对 ObservableCollection 的更新让我头疼,因为它们需要按一定的顺序进行。例如,当收到调用时,关联状态将在 ObservableCollection 中创建一个新条目。当状态更改时,新状态可能会尝试更新集合,即使不清楚是否已添加要更改的条目。状态的切换速度非常快,显然比更新集合的速度要快。
某种消息队列是一个可能/好的解决方案吗?如果是这样,如何在状态机或 ObservableCollection 的上下文中实现这样的消息队列。我并不是在寻找完整的解决方案,但任何我无法通过谷歌或 stackoverflow 轻松找到的信息将不胜感激。
编辑:大大改写了问题。
编辑:我为该问题添加了自己的解决方案,但会等待看看是否有人有更好的想法。
I am programming a TAPI application which uses the state pattern for dealing with the different states a TK can be in. Incoming and outgoing calls are recorded via an ObservableCollection
in a ListView
(call journal). The call data gets compared with contacts stored in a SQL-Server database to determine possible matches. That information is then used to update the call journal. All this in real time of course and all governed by/in the different states of the FSM (finite state machine).
To distinguish calls, I do use a call ID (which is provided by TAPI). When the phone rings or I start calling out, a new record including its call ID are added to the call journal and the customer database is searched for the number and certain data in the journal is updated accordingly. When proceeding through the different call states the application dynamically updates the journal (i.e. changing an icon that visually shows the state of the specific call, etc).
Exactly those updates to the ObservableCollection
are giving me headaches, as they need to happen in a certain order. For example, when receiving a call the associated state will create a new entry in the ObservableCollection
. When the state changes the new state might try to update the collection even though it is not clear weather the entry that is to be changed has been added already. The states happen to switch really fast, apparently faster than updating the collection can happen.
Would some kind of message queue be a possible/good solution? If so, how could such a message queue be implemented - in the context of either a state machine or an ObservableCollection
. I am not looking for complete solutions, but any information which I cannot easily find via google or stackoverflow would be appreciated.
Edit: greatly rephrased the question.
Edit: I added my own solution for the problem, but will wait and see if there is possibly someone with a better idea.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
你检查过
FirstOrDefault
的结果是否为null
吗?如果集合中不存在具有给定id
的元素,则可能会发生这种情况。例如:
或者您可以只调用
First
并查看是否收到InvalidOperationException
。--- 编辑 ---
我从您的评论中看到,您似乎正在同时从多个线程访问相同的
ObservableCollection
。如果是这种情况,您需要通过锁定来保护共享数据结构。一个线程完全有可能在另一个线程正在搜索新元素时开始插入新元素,从而导致各种未定义的行为。根据 ObservableCollection 的 MSN 文档:“任何实例成员都是不保证线程安全。”
至于调试,您可以“冻结”其他线程,这样您就可以只专注于感兴趣的线程,而无需过度“跳跃”。请参阅“线程”面板、右键单击菜单、“冻结”和“解冻”选项。
Have you checked whether the result of
FirstOrDefault
isnull
? This can happen if no element with givenid
exists in the collection.For example:
Or you could just call
First
and see if you getInvalidOperationException
.--- EDIT ---
I see from your comment that you seem to be accessing the same
ObservableCollection
from multiple threads concurrently. If that is the case, you need to protect the shared data structure through locking. It is entirely possible that one thread begins inserting a new element just at the moment the other one is searching for it, leading to all sorts of undefined behavior. According to MSN documentation for ObservableCollection:"Any instance members are not guaranteed to be thread safe."
As for debugging, you can "freeze" other threads and so you can concentrate only on the thread of interest without excessive "jumping". See the Threads panel, right-click menu, Freeze and Thaw options.
更新 ObservableCollection 是一个长时间运行的过程,至少与接收和处理 TAPI 事件相比是这样。这可能会导致竞争条件,其中必须编辑调用条目的调用状态无法找到该条目,因为它在实际必须添加调用的调用状态之前获得了写入/更新集合的锁定。此外,不按正确的顺序处理 TAPI 事件会破坏状态机。
我决定实现一个简化的命令模式。用于触发性能重型状态事务的 TAPI 事件被添加到线程安全、非阻塞且可观察的命令队列中。当命令入队时,队列类开始在新线程中“执行”(并出队)命令,也就是说,它触发有限状态机中的正确调用状态,直到队列中没有命令为止。如果有一个出队线程已经在运行,则不会创建新线程(多线程将再次导致竞争条件),并且队列类会阻止重入以确保在任何一次都只会执行一个命令。
基本上:所有 TAPI 事件(调用者)都按照发生的顺序尽可能快地添加到队列(客户端)中。然后,队列将 TAPI 信息中继到接收器(执行业务逻辑的有限状态机),花时间但确保信息以正确的顺序更新。
编辑:从.NET 4.0开始,您可以使用ConcurrentQueue (T) 类 达到相同的结果。
Updating the
ObservableCollection
is a long running process, at least compared to receiving and handling the TAPI-events. This can lead to race conditions, where a call state which would have to edit a call entry could not find the entry as it aquired the lock to writing/updating the collection prior to the call state that would actually have to add the call. Also, not handling the TAPI-events in the proper order would break the state machine.I decided to implement a simplified Command Pattern. The TAPI-Events, who used to trigger the performance heavy state transactions, get added to a thread safe, non-blocking and observable command queue. When a command gets enqueued the queue class starts "executing" (and dequeuing) the commands in a new thread, that is it is triggering the proper call states in the finite state machine, until there are no commands left in the queue. If there is a dequeuing thread already running no new thread is created (multi-threading would lead to race conditions again) and the queue class is blocking reentrancy to make sure that only one command will ever be executed at the any one time.
So basically: all TAPI-events (the invoker) are added to a queue (the client) in the order they are happening, as fast as possible. The queue then relays the TAPI information to the receiver, the finite state machine performing the business logic, taking its time but making sure the information gets updated in the proper order.
Edit: Starting from .NET 4.0 you can use the ConcurrentQueue(T) Class to achieve the same result.