簡體   English   中英

如何處理異步運行方法中的異常?

[英]How to handle exceptions in methods running asynchronous?

我正在構建一個跟蹤目錄的程序,並在目錄中創建新文件時將條目添加到mysql數據庫中。 不久前,我提出了一個問題 ,即如何讓FileSystemWatcher類獨立運行,同時填充要處理的操作的背景隊列,同時FileSystemWatcher繼續搜索。 我為此找到了解決方案,但想提出一個后續問題。 首先,下面是代碼:

這是觀察者的構造函數

FileSystemWatcher watcher = new FileSystemWatcher
{
    Path = directoryToWatch,
    IncludeSubdirectories = true,
    NotifyFilter = NotifyFilters.Attributes |
                   NotifyFilters.DirectoryName |
                   NotifyFilters.FileName,
    EnableRaisingEvents = true,
    Filter = "*.*"
};

watcher.Created += (OnDirectoryChange);

這是OnDirectoryChange方法:

public void OnDirectoryChange(object sender, FileSystemEventArgs e)
{
    try
    {
        bq.QueueTask(() => storage.Insert(Settings.Default.added_files, e.Name));
    }
    catch (StorageException se)
    {
        Console.WriteLine(se.Message);
        HandleException(se, se.Filename, se.Number);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Catch all exceptions");
    }
}

HandleException方法:

public void HandleException(Exception exceptionToHandle, string fileName = "empty", int number = 0)
{
    try
    {
        if (number < Acceptedammountofexceptions)
        {
            AddExceptionToStack(exceptionToHandle, fileName);
            RetryFailedFiles(new KeyValuePair<string, int>(fileName, number));
        }
        else if (number >= Acceptedammountofexceptions)
        {
            AddExceptionToCriticalList(exceptionToHandle, fileName);
            Console.WriteLine("Sorry, couldnt insert this file. See the log for more information.");
        }
    }
    catch (Exception e)
    {
        Console.WriteLine("Exception in HandleException");
        throw;
    }
}

RetryFailedFiles方法:

public void RetryFailedFiles(KeyValuePair<string, int> file)
{
    int count = file.Value + 1;
    try
    {
        bq.QueueTask(() => storage.Insert(Settings.Default.added_files, file.Key));
    }
    catch (StorageException se)
    {
        HandleException(se, file.Key, count);
        Console.WriteLine(se);
    }
}

如您所見,我想嘗試嘗試插入失敗的文件。 但是,目前不會重試失敗的文件。 老實說,我在自己的程序中有些失落。 如果有幫助,這里是insert語句的代碼。

public Task Insert(string tablename, string filename, int number=0)
{
    try
    {
        string query = "INSERT INTO " + tablename + " (FILE_NAME, " +
                       "Id) VALUES('" + filename + "', '" + Id + "')";

        using (MySqlConnection insertConn = new MySqlConnection(connectionString))
        {
            insertConn.Open();
            if (insertConn.State == ConnectionState.Open)
            {
                MySqlCommand insertCmd = new MySqlCommand(query, insertConn) {CommandType = CommandType.Text};
                while (insertCmd.Connection.State != ConnectionState.Open)
                {
                    // Spinlock
                }

                insertCmd.ExecuteNonQuery();
                insertConn.Close();
                return Task.CompletedTask;
            }
            StorageException se = new StorageException("Couldn't connect to database!", filename, number);
            Console.WriteLine(se.Message);
            return Task.FromException(se);
        }

    }
    catch (Exception ex)
    {

        Console.WriteLine(ex.Message);
        if (ex.Message.ToLower().Contains("duplicate"))
        {
            MessageBox.Show("Already exists", "Duplicate entry", MessageBoxButton.OK);
            return Task.CompletedTask;
        }
        else
        {
            StorageException se = new StorageException(ex.Message, filename, number);
            Console.WriteLine("Well");
            return Task.FromException(se);
        }
    }
}

如您所見,我已經在嘗試獲取一個異常,但是,如果這有意義的話,我似乎無法對其進行跟蹤。

那么我將如何處理拋出的異常,以確保文件名在第一次失敗時會再次嘗試?

編輯:Backgroundqueue類:

public class BackgroundQueue
{
    private Task previousTask = Task.FromResult(true);
    private object key = new object();
    public Task QueueTask(Action action)
    {
        lock (key)
        {
            previousTask = previousTask.ContinueWith(t => action()
                , CancellationToken.None
                , TaskContinuationOptions.None
                , TaskScheduler.Default);
            return previousTask;
        }
    }

    public Task<T> QueueTask<T>(Func<T> work)
    {
        lock (key)
        {
            var task = previousTask.ContinueWith(t => work()
                , CancellationToken.None
                , TaskContinuationOptions.None
                , TaskScheduler.Default);
            previousTask = task;
            return task;
        }
    }
}

目前尚不清楚bq是什么,但我猜測它實際上是Queue<Func<Task>>的包裝器-即, 被調用時啟動異步操作的事物隊列。 如果是這種情況,那么您的工作循環應該(如果打算保留順序)執行以下操作:

async Task RunQueue() {
    while(keepGoing) {
        if(no work) await work; // somehow, not shown; needs to be thread-safe

        Func<Task> nextAction = queue.ThreadSafeDequeue();
        try {
            await nextAction();
        } catch(Exception ex) {
            DoSomethingUseful(ex);
        }
    }
}

重要的是await等待異步完成,並重新引發觀察到的異常。

但是請注意, 您的具體情況下Insert正在做的工作是不是真正異步-它總是會完成(或失敗)同步。 不一定是問題,但是:這樣的數據訪問是異步的主要候選對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM