несколько потоков, используемых для поиска при вводе результатов при сбое

#c# #ios #multithreading #xamarin.ios #xamarin

#c# #iOS #многопоточность #xamarin.ios #xamarin

Вопрос:

Я использую xamarin / monotouch для приложения ios. На одном из экранов поиска я показываю всплывающее окно с результатами из адресной книги, когда пользователь вводит символы. У меня около 5000 контактов, и поэтому я выполняю фильтрацию в отдельном потоке, как показано ниже.

 Thread filterThread = new Thread( new ThreadStart( delegate {

                   // Some ui code


                    PersonSearchModal.find (findstr);
                    var personList = PersonSearchModal.getResult ();


                    // followed by some ui code to update pop up with person list


            }));
            filterThread.Start ();
  

и вот метод, который выполняет поиск 5000 контактов

 public static void find(string str)
        {
            lock (filterLock) {
                bool next = false;
                if (str.Length == 1)
                    clear ();

                if (results.Count == 0) {
                    next = true;
                } else {
                    PersonSearchResult r = results [results.Count - 1];
                    if (str.StartsWith (r.findstr, StringComparison.OrdinalIgnoreCase)) {
                        next = true;
                    }
                }
                if (next) {
                    findNext (str);
                } else {
                    findPrev ();
                }
            }
        }

public static void findNext(string str)
        {
            if (str.Trim ().Length == 0)
                return;

            Func<string, bool> filter = s => !String.IsNullOrWhiteSpace(s) amp;amp; s.StartsWith(str, StringComparison.OrdinalIgnoreCase);
            var peoplefrom = results.Count > 0 ? getResult() : people;
            var personList = peoplefrom.
                Where(p => filter(p.FirstName) 
                    || filter(p.LastName) 
                    || p.GetEmails().Select(e => e.Value).Any(filter)).ToList();

            var result = new PersonSearchResult ();
            result.findstr = str;
            result.people = personList;
            results.Add (result);
        }
  

Похоже, это работает очень хорошо, как поиск контактов в приложении mail. Однако, когда пользователь вводит быстро, приложение вылетает без каких-либо исключений. Кто-нибудь может предложить лучший способ планирования и синхронизации этих потоков поиска.

полный код

 public void filterContact(RectangleF rect, string searchstring)
        {

            ThreadPool.QueueUserWorkItem ( o => filter (rect, searchstring));

        }

        void filter(RectangleF rect, string searchstring)
        {

            lock (this) {
                try {
                    if (searchstring.Trim () == "") {
                        using (var pool = new NSAutoreleasePool ()) {
                            pool.InvokeOnMainThread (delegate {
                                if (DetailViewPopover != null) {
                                    DetailViewPopover.Dismiss (false);
                                }
                                return;
                            });
                        }
                        return;
                    }
                    string findstr = searchstring;
                    if (PersonSearchModal.people.Count == 0)
                        return;


                    PersonSearchModal.find (findstr);
                    var personList = PersonSearchModal.getResult ();


                    using (var pool = new NSAutoreleasePool ()) {
                        pool.InvokeOnMainThread (delegate {

                            if (personList.Count == 0) {
                                if (DetailViewPopover != null)
                                    DetailViewPopover.Dismiss (false);
                                return;
                            }

                            if (DetailViewPopover != null) {
                                _emailPickerController._searchName = searchstring;
                                PersonEmailPickerViewControllerSource PEsource = (PersonEmailPickerViewControllerSource)_emailPickerController.TableView.Source;
                                PEsource.personList = personList;
                                _emailPickerController.TableView.ReloadData ();

                                if (!DetailViewPopover.PopoverVisible) {
                                    DetailViewPopover.PopoverContentSize = new SizeF (400, 300);
                                    DetailViewPopover.PresentFromRect (rect, this.View, UIPopoverArrowDirection.Up, true);
                                } 

                            } else {
                                _emailPickerController = PersonEmailPickerViewController.GetInstance ();
                                _emailPickerController._searchName = searchstring;
                                PersonEmailPickerViewControllerSource PEsource = (PersonEmailPickerViewControllerSource)_emailPickerController.TableView.Source;
                                PEsource.personList = personList;
                                _emailPickerController.TableView.ReloadData ();
                                DetailViewPopover = new UIPopoverController (_emailPickerController);
                                DetailViewPopover.PopoverContentSize = new SizeF (400, 300);
                                DetailViewPopover.PresentFromRect (rect, this.View, UIPopoverArrowDirection.Up, true);
                            }
                        });
                    }

                } catch (System.Exception) {

                }
            }

        }
  

Комментарии:

1. Сбой — это «ошибка malloc *** для объекта 0x double free». Я не смог найти, где это происходит, хотя

Ответ №1:

Это похоже на условие гонки в вашем коде. Я предполагаю, что происходит следующее:

  • Вы запускаете несколько потоков filterThread, каждый из которых работает с одними и теми же данными без какой-либо синхронизации.
  • При использовании BeginInvokeOnMainThread основной поток будет работать с одними и теми же данными, которые уже были изменены одновременно из нескольких потоков (и, вероятно, модифицируются, пока основной поток также использует данные), и вы в конечном итоге отправляете мусор в пользовательский интерфейс из основного потока, что в конечном итоге приводит к сбою.

Попробуйте убедиться, что вы одновременно запускаете только один filterThread, и никогда, пока основной поток обращается к тем же данным, которые использует filterThread.

Комментарии:

1. я внес изменения в код для блокировки критической области, как указано выше. Не видел сбоя.