我遇到了Rx的一些问题。我需要在处理新线程中的每个项目后将结果发送到主线程。我用其他方式做到了。如何用Rx解决这个任务?这是代码:
Observable.ToAsync<string, IEnumerable<UserState>>(Run)(path)
.ObserveOnDispatcher<IEnumerable<UserState>>()
.Subscribe(
(o) =>
{ // need to run in Main Thread
foreach (var item in o)
{
WriteLog(item.StatusCode, item.Url);
}
},
(ex) =>{ },
() =>{ } );
// need to run in New Thread
private IEnumerable<UserState> Run(string sitemap)
{
....
foreach (var url in urls)
{
var result = new UserState
{
Url = url.Value,
TotalItems = urls.Count
};
....
yield return result;
}
}
答案 0 :(得分:1)
如果你能描述一下你的问题,那将是件好事。
我现在可以告诉你的是,你有几个潜在的问题。
首先,使用ObserveOnDispatcher
意味着使用System.Windows.Threading.Dispatcher
(通常使用WPF创建)。如果您在WPF之外运行代码,它实际上意味着“当前线程”,这可能导致您的订阅无法在当前线程繁忙时运行。换句话说,您可能会创建一个死锁。
我在WPF和LINQPad中运行你的代码,它在WPF中运行良好,但在LINQPad中死锁。如果我在另一个线程上观察到它在LINQPad中工作正常并且在WPF中失败。
其次,您将迭代器方法转换为异步observable,并且无法按预期工作。在实际遍历可枚举之前,迭代器实际上不会运行任何代码。基本上你几乎立即从Run
返回,你只在Run
代码中执行Subscribe
方法的主体 - 这是错误的线程!
您需要做的是强制立即执行可枚举 - 至少 - 将代码更改为:
private UserState[] Run(string sitemap)
{
...
Func</* url type */, UserState> create = url =>
{
var result = new UserState
{
Url = url.Value,
TotalItems = urls.Count
};
....
return result;
};
return (from url in urls select create(url)).ToArray();
}
您的主要代码需要进行一些清理:
Observable.ToAsync<string, UserState[]>(Run)(path)
.ObserveOnDispatcher()
.Subscribe(o =>
{
foreach (var item in o)
{
WriteLog(item.StatusCode, item.Url);
}
});
如果有任何帮助,请告诉我。
编辑:根据评论中的OP请求添加了示例FromEventPattern
代码。
以下是Windows窗体使用FromEventPattern
的示例。第一部分创建了一种在表单关闭时清理订阅的方法。
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Create a collection of IDisposable
// to allow clean-up of subscriptions
var subscriptions =
new System.Reactive.Disposables.CompositeDisposable();
var formClosings = Observable
.FromEventPattern<FormClosingEventHandler, FormClosingEventArgs>(
h => this.FormClosing += h,
h => this.FormClosing -= h);
// Add a subscription that cleans up subscriptions
// when the form closes
subscriptions.Add(
formClosings
.Subscribe(ea => subscriptions.Dispose()));
下一部分会在图片框中监视鼠标拖动并创建消息,让用户知道他们拖动了多远。
var pictureBox1MouseDowns = Observable
.FromEventPattern<MouseEventHandler, MouseEventArgs>(
h => pictureBox1.MouseDown += h,
h => pictureBox1.MouseDown -= h);
var pictureBox1MouseMoves = Observable
.FromEventPattern<MouseEventHandler, MouseEventArgs>(
h => pictureBox1.MouseMove += h,
h => pictureBox1.MouseMove -= h);
var pictureBox1MouseUps = Observable
.FromEventPattern<MouseEventHandler, MouseEventArgs>(
h => pictureBox1.MouseUp += h,
h => pictureBox1.MouseUp -= h);
var pictureBox1MouseDrags =
from md in pictureBox1MouseDowns
from mm in pictureBox1MouseMoves.TakeUntil(pictureBox1MouseUps)
let dx = mm.EventArgs.Location.X - md.EventArgs.Location.X
let dy = mm.EventArgs.Location.Y - md.EventArgs.Location.Y
select new Point(dx, dy);
var pictureBox1MouseDragMessages =
from md in pictureBox1MouseDrags
let f = "You've dragged ({0}, {1}) from your starting point"
select String.Format(f, md.X, md.Y);
下一部分将跟踪点击按钮的次数,并创建要显示给用户的消息。
var button1ClickCount = 0;
var button1Clicks = Observable
.FromEventPattern(
h => button1.Click += h,
h => button1.Click -= h);
var button1ClickCounts =
from c in button1Clicks
select ++button1ClickCount;
var button1ClickMessages =
from cc in button1ClickCounts
let f = "You clicked the button {0} time{1}"
select String.Format(f, cc, cc == 1 ? "" : "s");
最后,两个消息obervables合并在一起并被订阅,将消息放在标签中。
var messages = pictureBox1MouseDragMessages
.Merge(button1ClickMessages);
// Add a subscription to display the
// merged messages in the label
subscriptions.Add(
messages
.Subscribe(m => label1.Text = m));
}
}
请记住,所有这些都驻留在表单的构造函数中,并且不使用任何模块级别字段或属性,并且在表单关闭时将删除所有事件处理程序。很整洁。
答案 1 :(得分:1)
你想在其他一些后台线程上生成IEnumerable,然后在主UI线程中处理这个枚举中的每个用户对象,如果这种理解是正确的,那么你可以这样做:
var users = Run(path); //NOTE: This doesn't execute your run method yet, Run will only execute when you start enumerating the users values
users.ToObservable(System.Concurrency.Scheduler.ThreadPool) //The enumerator will be scheduled on separate thread
.ObserveOn(frm) //Observe on UI thread of win form
.Subscribe(s => {}) //This will run in UI thread for each user object