我正在构建一个程序,用于跟踪目录并在目录中创建新文件时向 mysql 数据库添加一个条目。不久前我发布了一个question了解如何让 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);
}
}
如您所见,我想重试插入失败的文件一定次数。但是,此时不会重试失败的文件。说实话,我对自己的计划感到有点迷失。如果有帮助,这里是插入语句的代码。
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);
}
}
}
如您所见,我已经在尝试获取异常,但我似乎无法追踪它(如果这有意义的话)。
那么我该如何处理抛出的异常,以确保文件名在第一次失败时会再次尝试?
编辑: 背景队列类:
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
正在完成的工作实际上并不是异步的 - 它总是同步完成(或失败)。这不一定是一个问题,但是:像这样的数据访问是异步的主要候选者。
关于c# - 如何处理异步运行方法中的异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49509748/