C# 实时迭代应用程序的体系结构

C# 实时迭代应用程序的体系结构,c#,.net,database,wcf,sync,C#,.net,Database,Wcf,Sync,很抱歉提出这个抽象的问题,但我正在寻找一些关于在循环中执行一些等效操作的应用程序类型的示例/建议/文章,循环的每个迭代都应该在一定的时间段(例如,10秒)内公开其结果 我的应用程序在外部WCF服务和本地数据库之间进行数据同步。在每次迭代中,应用程序都会检索将请求传递给WCF服务的数据更改,并将更改放入数据库,反之亦然。这个应用程序最困难的一个要求是迭代应该每10秒启动一次 因此,问题就出现了。我如何保证迭代完成的时间不超过10秒 我猜这种类型的应用程序称为实时应用程序(在许多实时操作系统中) 我

很抱歉提出这个抽象的问题,但我正在寻找一些关于在循环中执行一些等效操作的应用程序类型的示例/建议/文章,循环的每个迭代都应该在一定的时间段(例如,10秒)内公开其结果

我的应用程序在外部WCF服务和本地数据库之间进行数据同步。在每次迭代中,应用程序都会检索将请求传递给WCF服务的数据更改,并将更改放入数据库,反之亦然。这个应用程序最困难的一个要求是迭代应该每10秒启动一次

因此,问题就出现了。我如何保证迭代完成的时间不超过10秒

我猜这种类型的应用程序称为实时应用程序(在许多实时操作系统中)

我们使用的DAL组件会随机作用于连接超时行为。因此,DB操作可能需要10秒以上的时间

以下是一次迭代的估计代码:

        Stopwatch s1 = new Stopwatch();
        s1.Start();
        Parallel.ForEach(Global.config.databases, new ParallelOptions { MaxDegreeOfParallelism = -1 }, (l) =>            
        {
            Console.WriteLine("Started for {0}", l.key.name);                
            DB db = new DB(l.connectionString);

            DateTime lastIterationTS = GetPreviousIterationTS(l.id);

            ExternalService serv = new ExternalService(l.id);
            List<ChangedData> ChangedDataDb = db.GetChangedData(DateTime.Now.AddSeconds((lastIterationTS == DateTime.MinValue) ? -300 : -1 * (DateTime.Now - lastIterationTS).Seconds));

            List<Data> ChangedDataService = serv.GetModifiedData();                

                    Action syncDBChanges = new Action(() =>
                        {
                            // Изменения в БД                                   
                            foreach (ChangedData d in ChangedDataDb)
                            {
                                try
                                {
                                    // ...
                                    // analyzing & syncing
                                }
                                catch (Exception e)
                                {
                                    logger.InfoEx("Exception_SyncDatabase", e.ToString());
                                }
                            }
                        }
                    );

                    Action syncService = new Action(() =>
                    {                            
                        foreach (Data d in ChangedDataService)
                        {
                            try
                            {
                                // ...
                                // analyzing & syncing
                            }
                            catch (Exception e)
                            {
                                logger.InfoEx("Exception_SyncService", e.ToString());
                            }
                        }
                    });

                    List<WaitHandle> handles = new List<WaitHandle>();
                    IAsyncResult ar1 = syncDBChanges.BeginInvoke(syncDBChanges.EndInvoke, null);
                    IAsyncResult ar2 = syncService.BeginInvoke(syncService.EndInvoke, null);

                    handles.Add(ar1.AsyncWaitHandle);
                    handles.Add(ar2.AsyncWaitHandle);

                    WaitHandle.WaitAll(handles.ToArray(), (int)((Global.config.syncModifiedInterval - 1) * 1000));
                    SetCurrentIterationTS(l.id);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    logger.InfoEx("Exception_Iteration", e.ToString());
                    continue;
                }
            }
            logger.InfoEx("end_Iteration", IterationContextParams);
        }
        );
        s1.Stop();
        Console.WriteLine("Main iteration done for {0}...", s1.Elapsed);        
秒表s1=新秒表();
s1.Start();
Parallel.ForEach(Global.config.databases,新的ParallelOptions{maxdegreeofpparallelism=-1},(l)=>
{
WriteLine(“为{0}启动”,l.key.name);
DB=新的DB(l.连接字符串);
DateTime lastIterationTS=GetPreviousIterationTS(l.id);
ExternalService serv=新的ExternalService(l.id);
List changedatadb=db.GetChangedData(DateTime.Now.AddSeconds((lastIterationTS==DateTime.MinValue)?-300:-1*(DateTime.Now-lastIterationTS.Seconds));
List ChangedDataService=serv.GetModifiedData();
动作syncDBChanges=新动作(()=>
{
// Изменения в БД                                   
foreach(changedDataB中的changedDataD)
{
尝试
{
// ...
//分析与同步
}
捕获(例外e)
{
InfoEx(“异常同步数据库”,例如ToString());
}
}
}
);
动作同步服务=新动作(()=>
{                            
foreach(ChangedDataService中的数据d)
{
尝试
{
// ...
//分析与同步
}
捕获(例外e)
{
InfoEx(“异常同步服务”,例如ToString());
}
}
});
列表句柄=新列表();
IAsyncResult ar1=syncDBChanges.BeginInvoke(syncDBChanges.EndInvoke,null);
IAsyncResult ar2=syncService.BeginInvoke(syncService.EndInvoke,null);
Add(ar1.AsyncWaitHandle);
Add(ar2.AsyncWaitHandle);
WaitHandle.WaitAll(handles.ToArray(),(int)((Global.config.syncModifiedInterval-1)*1000));
SetCurrentIterationTS(l.id);
}
捕获(例外e)
{
控制台写入线(e.Message);
InfoEx(“异常迭代”,例如ToString());
继续;
}
}
InfoEx(“end_Iteration”,IterationContextParams);
}
);
s1.停止();
WriteLine(“为{0}完成的主迭代…”,s1.appeased);

我通常将更新周期与实际计时器分开

计时器做两件事:

1) 如果更新未运行,将启动它

2) 如果服务已在运行,请为其设置一个标志以继续运行

更新周期:

1) 设置运行标志

2) 进行更新

3) 将运行标志设置为false


< p>4)如果继续运行设置为1,则

可以考虑几个选项…

  • 如果迭代时间超过10秒,则终止该迭代,并希望下一次迭代能够完成该过程。这种方法的问题是,很有可能所有迭代都不会完成,因此同步过程永远不会发生。我会推荐以下选项

  • 如果迭代时间超过10秒,请等待它完成并跳过下一次迭代。这样可以确保流程至少完成一次。以下是供参考的简化代码示例

    class Updater
    {
        Timer timer = new Timer();
        public object StateLock = new object();
        public string State;
    
        public Updater()
        {
            timer.Elapsed += timer_Elapsed;
            timer.Interval = 10000;
            timer.AutoReset = true;
            timer.Start();
        }
    
        void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (State != "Running")
            {
                Process();
            }
        }
    
        private void Process()
        {
            try
            {
                lock (StateLock)
                {
                    State = "Running";
                }
    
                // Process
    
                lock (StateLock)
                {
                    State = "";
                }
            }
            catch
            {
                throw;
            }
        }
    }
    
  • 是一个优秀的.NET平台调度程序,我认为它可以满足您的需要

    • 如果你想干掉一份工作,你可以实施。您应该能够在Interupt方法中添加一些清理代码来处理任何db连接
    • 如果您想完成一个作业,但只有在最后一个作业完成后才开始另一个作业(我认为这是更好的选择),那么可以实现接口

    您可能希望了解.Net中可用的各种计时器对象:

    我个人喜欢
    System.Threading.Timer
    ,因为您可以轻松使用lambdas,并且如果创建单独的回调,它允许传递状态对象

    我还建议使用
    System.Threading.Tasks
    库,因为它允许您在这种情况下优雅地处理取消
    class Program
    {
        static void Main(string[] args)
        {
            Updater updater = new Updater();
            Console.ReadLine();
        }
    }
    
    CancellationTokenSource cancelSource = new CancellationTokenSource();
    System.Threading.Timer timer = new System.Threading.Timer(callback =>
    {
        //start sync
        Task syncTask = Task.Factory.StartNew(syncAction =>
            {
                using (SqlConnection conn = 
                    new SqlConnection(
                       ConfigurationManager.ConnectionStrings["db"].ConnectionString))
                {
                    conn.Open();
                    using (SqlCommand syncCommand = new SqlCommand
                    {
                        CommandText = "SELECT getdate() \n WAITFOR DELAY '00:11'; ",
                        CommandTimeout = 600,
                        Transaction = conn.BeginTransaction(),
                        Connection = conn
                    })
                    {
                        try
                        {
                            IAsyncResult t = syncCommand.BeginExecuteNonQuery();
                            SpinWait.SpinUntil(() => 
                                (t.IsCompleted || cancelSource.Token.IsCancellationRequested));
                            if (cancelSource.Token.IsCancellationRequested && !t.IsCompleted)
                                syncCommand.Transaction.Rollback();
    
                        }
                        catch (TimeoutException timeoutException)
                        {
                            syncCommand.Transaction.Rollback();
                            //log a failed sync attepmt here
                            Console.WriteLine(timeoutException.ToString());
                        }
                        finally
                        {
                            syncCommand.Connection.Close();
                        }
                    }
                }
            }, null, cancelSource.Token);
        //set up a timer for processing in the interim, save some time for rollback
        System.Threading.Timer spinTimer = new System.Threading.Timer(c => {
            cancelSource.Cancel();
        }, null, TimeSpan.FromMinutes(9), TimeSpan.FromSeconds(5)); 
    
        //spin here until the spintimer elapses;
        //this is optional, but would be useful for debugging.
        SpinWait.SpinUntil(()=>(syncTask.IsCompleted || cancelSource.Token.IsCancellationRequested));
        if (syncTask.IsCompleted || cancelSource.Token.IsCancellationRequested)
            spinTimer.Dispose();
    
    }, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(10));
    
    class DatabaseUpdater
    {
        private readonly Timer _timer;
        private List<Thread> _threads;
        private readonly List<DatabaseConfig> _dbConfigs;
    
        public DatabaseUpdater(int seconds, List<DatabaseConfig> dbConfigs)
        {
            _timer = new Timer(seconds * 1000);
            _timer.Elapsed += TimerElapsed;
            _dbConfigs = dbConfigs;
        }
    
        public void Start()
        {
            StartThreads();
            _timer.Start();
        }
    
        public void Stop()
        {
            _timer.Stop();
            StopThreads();
        }
    
        void TimerElapsed(object sender, ElapsedEventArgs e)
        {
            StopThreads();
            StartThreads();
        }
    
        private void StartThreads()
        {
            var newThreads = new List<Thread>();
            foreach (var config in _dbConfigs)
            {
                var thread = new Thread(DoWork);
                thread.Start(config);
                newThreads.Add(thread);
            }
    
            _threads = newThreads;
        }
    
        private void StopThreads()
        {
            if (_threads == null) return;
    
            var oldThreads = _threads;
            foreach (var thread in oldThreads)
            {
                thread.Abort();
            }
        }
    
        static void DoWork(object objConfig)
        {
            var dbConfig = objConfig as DatabaseConfig;
            if (null == dbConfig) return;
    
            var n = GetRandomNumber();
    
            try
            {
                ConsoleWriteLine("Sync started for : {0} - {1} sec work.", dbConfig.Id, n);
    
                // update/sync db
                Thread.Sleep(1000 * n);
    
                ConsoleWriteLine("Sync finished for : {0} - {1} sec work.", dbConfig.Id, n);
            }
            catch (Exception ex)
            {
                // cancel/rollback db transaction
                ConsoleWriteLine("Sync cancelled for : {0} - {1} sec work.",
                    dbConfig.Id, n);
            }
        }
    
        static readonly Random Random = new Random();
    
        [MethodImpl(MethodImplOptions.Synchronized)]
        static int GetRandomNumber()
        {
            return Random.Next(5, 20);
        }
    
        [MethodImpl(MethodImplOptions.Synchronized)]
        static void ConsoleWriteLine(string format, params object[] arg)
        {
            Console.WriteLine(format, arg);
        }
    }
    
    static void Main(string[] args)
    {
        var configs = new List<DatabaseConfig>();
        for (var i = 1; i <= 3; i++)
        {
            configs.Add(new DatabaseConfig() { Id = i });
        }
    
        var databaseUpdater = new DatabaseUpdater(10, configs);
        databaseUpdater.Start();
    
        Console.ReadKey();
    
        databaseUpdater.Stop();
    }