Я ищу способ наложить много работы на один фоновый поток в Silverlight - PullRequest
1 голос
/ 02 апреля 2011

У меня интересная проблема с приложением для Windows Phone 7. Прямо сейчас я отправляю около двух десятков или около того запросов на небольшие (несколько килобайт) xml-файлы, чтобы сохранить быстро меняющуюся информацию (точнее, адреса шин). У меня есть таймер каждые 10 секунд для асинхронной отправки запросов.

Полученные данные обрабатываются с помощью асинхронного обратного вызова для проверки того, что запрос вернулся с файлом, и сохраняют файл в IsolatedStorage. Когда необходимы данные (с помощью модели представления пользовательского интерфейса), файл считывается из IsolatedStorage, анализируется во внутренние объекты (в основном, GeoCoordinate и некоторые строки) и используется.

1) Я немного столкнулся с ограничениями центрального процессора телефона. Если я проанализирую все данные шины, чтобы определить, какие автобусные маршруты активны в потоке пользовательского интерфейса, поток заметно замедлится. Я переместил работу в bakground работник, как это:

public class RunningWorkerHelper
{
    public BusRouteIdModel Route { get; set; }
    public Visibility Visible { get; set; }
}

public class RunningWorker
{
    BackgroundWorker bw;
    Queue<BusRouteIdModel> workQueue;

    public RunningWorker()
    {

        bw = new BackgroundWorker();
        workQueue = new Queue<BusRouteIdModel>();

        bw.DoWork += CheckIfRunning;
        bw.RunWorkerCompleted += CheckRunningCompleted; 
    }

    public void QueueWorkItem(BusRouteIdModel route)
    {
        workQueue.Enqueue(route);
        StartWorkItem();
    }

    void StartWorkItem()
    {
        if (!bw.IsBusy && workQueue.Count > 0)
            bw.RunWorkerAsync(workQueue.Dequeue());
    }

    void CheckIfRunning(object sender, DoWorkEventArgs args)
    {
        BusRouteDataService server= BusRouteDataService.Current;
        var route = (BusRouteIdModel)args.Argument;
        if (route == null) return;

        var vehicleFile = server.GetBusVehiclesFile(route);
        var vehiclesOnRoute = RouteDataParser.ExtractBusVehicles(vehicleFile);
        var helper = new RunningWorkerHelper { Route = route };
        if (vehiclesOnRoute.Count > 0)
        {
            helper.Visible = Visibility.Visible;
        }
        else
        {
            helper.Visible = Visibility.Collapsed;
        }
        args.Result = helper;
    }

    void CheckRunningCompleted(object sender, RunWorkerCompletedEventArgs args)
    {
        var helper = (RunningWorkerHelper)args.Result;
        if (helper != null)
            helper.Route.IsRunning = helper.Visible;
        StartWorkItem();
    }

}

вот BusRouteIdModel (для справки)

public class BusRouteIdModel : BusRouteId, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    Visibility _isRunning;
    /// <summary>
    /// True when there is a bus active on this route
    /// </summary>
    public Visibility IsRunning
    {
        get { return _isRunning; }
        set
        {
            if (value == _isRunning) return;
            _isRunning = value;
            OnPropertyChanged(new PropertyChangedEventArgs("IsRunnning"));
       }
    }

    /// <summary>
    /// True while path data for the route isn't available yet
    /// </summary>
    Visibility _isLoading;
    public Visibility IsLoading
    {
        get { return _isLoading; }
        set
        {
            if (value == _isLoading) return;
            _isLoading = value;
            OnPropertyChanged(new PropertyChangedEventArgs("IsLoading"));
        }
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, args);
    }
}

public class BusRouteId
{
    public String Uid { get; set; }
    public String Name { get; set; }
}

Это данные, связанные с элементом списка на странице (я использую MVVM Light Toolkit):

    public const string RouteIdsPropertyName = "RouteIds";
    private ObservableCollection<BusRouteIdModel> _routeIds = null;
    public ObservableCollection<BusRouteIdModel> RouteIds
    {
        get { return _routeIds; }
        private set
        {
            if (_routeIds == value) return;
            var oldValue = _routeIds;
            _routeIds = value;
             RaisePropertyChanged(RouteIdsPropertyName);
        }
    }

И я называю своего работника вот так (идентификатор args.Uid - это автобусный маршрут, который получил новые данные):

foreach (var route in RouteIds)
    if (route.Uid == args.Uid) 
        runningWorker.QueueWorkItem(route);

2) Таинственным образом, изменение IsRunning не отражается в пользовательском интерфейсе. Что я упустил?

3) Как я могу обобщить этот Queue + BackgroundWorker для решения других задач? (Я бы использовал библиотеку пула задач, но в Silverlight ее нет.) Я хочу иметь одного работника, чтобы поток пользовательского интерфейса не истощался на странице карты.

1 Ответ

3 голосов
/ 02 апреля 2011

1) Не вопрос?

2) IsRunning в пользовательском интерфейсе не обновляется из-за опечатки:

OnPropertyChanged(new PropertyChangedEventArgs("IsRunnning"))

Слишком много п

3) Для таких потоков попробуйте ThreadPool - см. http://wildermuth.com/2011/01/11/Architecting_WP7_-_Part_9_of_10_Threading

...