0

DataGridを使用したWPFプロジェクトがあります。MVVMパターンを使用しています。それは私のVMの一部です:

class LibraryViewModel
{
    #region Members
    //private SimpleLibDBEntities _database;
    ObservableCollection<BooksViewModel> _books = new ObservableCollection<BooksViewModel>();
    int count = 0;
    int sizeOfdb = 1000000;
    #endregion

    public ObservableCollection<BooksViewModel> Books
    {
        get
        {
            return _books;
        }
        set
        {
            _books = value;
        }
    }
    public LibraryViewModel()
    {
        Task task = Task.Factory.StartNew(Generator);
    }
    private void Generator()
    {
        for (count = 0; count < sizeOfdb; count++)
        {
            _books.Add(new BooksViewModel { Book = new BooksSet { Id = count, Title = "Title"+count, Author = "Author", Publisher = "Publisher", Year = 1000, Note = "Note" } });
        }
    }

それは機能しますが、私のDataGridは、私のint sizeOfdb = 1000000要素ではなく、約50 000 -100 000要素(ランダム)しか表示しません。なぜそれがうまくいくのですか?それを修復する方法は?(「タスク」なしですべてが正常に機能します)

そして、この例でasync / awaitをどのように使用できますか?そんな感じ?(動作しません。Dispathcherを使用してみてください?)

public LibraryViewModel()
{
    GeneratorAsync();
}
private async void GeneratorAsync()
{
        await Task.Factory.StartNew(()=>{
            for (count = 0; count < sizeOfdb; count++)
            {
                _books.Add(...);
            }
        });
    }
4

1 に答える 1

4

ここでの基本的な問題は、ObservableCollection完全にスレッドセーフではないことです。より具体的には、あるスレッドからコレクションを変更しながら別のスレッドから読み取りを行うのは安全ではない可能性があり、コレクションを変更するスレッドでObservableCollectionそのCollectionChangedイベントを発生させます。

このインスタンスで起こっていると推測しているのは、UI が作成されてバインディングが発生する前に、バックグラウンド タスクが多数の要素を作成し、それらをコレクションに追加することです。その時点で、コレクション内の要素が DataGrid に追加されます。しかし、次の項目が追加され、CollectionChangedイベントがスレッド プール スレッドで発生すると、DataGridのイベント ハンドラがスレッド プール スレッドで実行され、スレッド アフィニティに違反します。これにより、タスクを終了させる例外が発生します (推測では、デバッガーでブレーク オン ユーザー未処理の例外をオンにして実行すると、これが表示されるはずです)。

コレクション アイテムの生成にコストがかかることが予想される場合 (たとえば、実際のバージョンでデータベース アクセスを伴う場合)、バックグラウンド スレッドでそれを実行してから、それらをObservableCollectionUI スレッドに追加する必要がありますが、この質問にはこれについての良い議論があります

ファイア アンド フォーゲット タスクの開始をファイア アンド フォーゲットの「async void」でラップしようとしているだけなので、await/async は試行された定式化で実際には役に立たないことがわかります。UI スレッドから作成を移動しようとしている場合は、次のようにします。

private async void AddBooks()
{
    var books = await Task.Run(() => GetBooks());
    foreach (var book in books)
    {
        _books.Add(book);
    }
}

private List<BooksViewModel> GetBooks()
{
    List<BooksViewModel> books = new List<BooksViewModel>();
    for (count = 0; count < sizeOfdb; count++)
    {
        books.Add(new BooksViewModel { Book = new BooksSet { Id = count, Title = "Title" + count, Author = "Author", Publisher = "Publisher", Year = 1000, Note = "Note" } });
    }
    return books;
}

この場合、作成はスレッド プール スレッドで実行され、待機によって UI スレッドに戻ります。

于 2013-03-09T12:26:03.073 に答える