gpt4 book ai didi

.net - MVVM 应用程序中的并发架构

转载 作者:行者123 更新时间:2023-12-03 10:15:24 25 4
gpt4 key购买 nike

我有一个客户端/服务器类型的应用程序设置,类似于 bittorrent 下载程序。但是,种子是远程发送到客户端的。

共享数据的主要部分是要下载的文件(种子)列表。

我必须同时处理这些情况:

  • 服务器(通过 WCF)发送要下载的文件更新列表,这意味着一些新文件将被添加到列表中,一些新文件将从列表中删除(并且一些保持不变)
  • 同时文件可能会完成下载/更改状态,因此列表中的项目需要在本地更新为新状态
  • 客户端的本地事件可能会导致列表中的某些项目过期,因此应将其删除

  • 我正在使用 MVVM 架构,但我相信 View 模型应该紧密映射到 View ,所以我添加了一个“服务”层,目前是一堆单例(我知道)。其中一个充当所述列表的共享资源,因此我有一个由多个线程更新的集合。

    我想远离单例,转而支持依赖注入(inject)和不可变对象(immutable对象),以减少我一直看到的死锁、“删除/分离对象”和数据完整性错误。

    但是,我不知道在哪里“保留”列表以及如何管理来自不同线程的传入事件,这些线程可能会取消/否定/覆盖列表的当前处理。

    我正在寻找有关在高级别处理这种情况的指示。

    我对列表中的项目使用 Entity Framework ,因为数据也需要持久化。

    最佳答案

    我最近为 Windows 服务检查器做了类似的事情。它最终也很容易实现。

    在您的情况下,我认为需要以下内容。

    文件 - 它的唯一目的是下载文件并通知更改。
    FileManager - 维护文件列表并添加新的,删除等。

    public class File : INotifyPropertyChanged
    {
    private readonly string _fileName;
    private Thread _thread;
    private Task _task;
    private bool _cancelled;

    private TaskStatus _taskStatus;
    private int _taskProgress;
    private int _taskTotal;

    public event PropertyChangedEventHandler PropertyChanged;

    public File(string fileName)
    {
    _fileName = fileName;
    TaskStatus = TaskStatus.NotStarted;
    }

    public TaskStatus TaskStatus
    {
    get { return _taskStatus; }
    private set
    {
    _taskStatus = value;
    PropertyChanged.Raise(this, x => x.TaskStatus);
    }
    }

    public int TaskProgress
    {
    get { return _taskProgress; }
    private set
    {
    _taskProgress = value;
    PropertyChanged.Raise(this, x => x.TaskProgress);
    }
    }
    public int TaskTotal
    {
    get { return _taskTotal; }
    private set
    {
    _taskTotal = value;
    PropertyChanged.Raise(this, x => x.TaskTotal);
    }
    }

    public void StartTask()
    {
    _cancelled = false;

    //.Net 4 - task parallel library - nice
    _task = new Task(DownloadFile, TaskCreationOptions.LongRunning);
    _task.Start();

    //.Net Other
    _thread = new Thread(DownloadFile);
    _thread.Start();
    }

    public void CancelTask()
    {
    _cancelled = true;
    }

    private void DownloadFile()
    {
    try
    {
    TaskStatus = TaskStatus.Running;

    var fileLength = _fileName.Length;
    TaskTotal = fileLength;

    for (var i = 0; i < fileLength; i++)
    {
    if (_cancelled)
    {
    TaskStatus = TaskStatus.Cancelled;
    return;
    }

    //Some work to download the file
    Thread.Sleep(1000); //sleep for the example instead

    TaskProgress = i;
    }

    TaskStatus = TaskStatus.Completed;

    }
    catch (Exception ex)
    {
    TaskStatus = TaskStatus.Error;
    }
    }
    }

    public enum TaskStatus
    {
    NotStarted,
    Running,
    Completed,
    Cancelled,
    Error
    }

    public static class NotifyPropertyChangedExtention
    {
    public static void Raise<T, TP>(this PropertyChangedEventHandler pc, T source, Expression<Func<T, TP>> pe)
    {
    if (pc != null)
    {
    pc.Invoke(source, new PropertyChangedEventArgs(((MemberExpression)pe.Body).Member.Name));
    }
    }
    }

    这样做的好处是您永远不需要从后台线程更新 UI。您正在更新的是只读属性,只有后台类也会写入。此类之外的任何内容都只能读取,因此您不必担心锁定。 UI 绑定(bind)系统将在引发 PropertyChanged 时收到属性已更改的通知,然后读取该值。

    现在为经理
    public class FileManager
    {
    public ObservableCollection<File> ListOfFiles { get; set; }

    public void AddFile(string fileName)
    {
    var file = new File(fileName);
    file.PropertyChanged += FilePropertyChanged;
    file.StartTask();
    ListOfFiles.Add(file);
    }

    void FilePropertyChanged(object sender, PropertyChangedEventArgs e)
    {
    if (e.PropertyName == "TaskStatus")
    {
    var file = (File) sender;
    if (file.TaskStatus==TaskStatus.Completed)
    {
    RemoveFile(file);// ??? automatically remove file from list on completion??
    }
    }
    }

    public void RemoveFile(File file)
    {
    if (file.TaskStatus == TaskStatus.Running)
    {
    file.CancelTask();
    }
    //unbind event
    file.PropertyChanged -= FilePropertyChanged;
    ListOfFiles.Remove(file);
    }
    }

    现在,您在 View 模型中需要做的就是从 FileManager 中公开 ListOfFiles,这是一个可观察的集合。来自它的通知将让绑定(bind)系统知道 UI 何时需要更新。

    只需将 ListOfFiles 绑定(bind)到 ListView 或类似的,为 File 类添加一个数据模板,这将使 ListView 知道如何呈现每个文件。

    您的 WCF 服务器和 View 模型应该具有对相同文件管理器的引用,WCF 添加和删除文件, View 模型使 ListOfFiles 可用于 UI。

    这只是一个粗略的技巧来理解这个概念。你需要添加你认为合适的东西。

    让我知道这是否有帮助。

    关于.net - MVVM 应用程序中的并发架构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6677538/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com