| View previous topic :: View next topic |
| Author |
Message |
cneuwirt
Joined: 24 Jun 2006 Posts: 5
|
Posted: Sun Apr 15, 2007 2:53 pm Post subject: SynchronizeFacility available |
|
|
I just committed a SynchronizeFacility which does the following
- Recognizes the implicit sync interface ISynchronizeInvoke and
coordinates it api. This eliminates the code pollution of having to
call InvokeRequired and pass a delegate to the currrent method.
The most common use is to prevent the dread thread access
violation which using WinForms, but works for any component
implementing the interface.
- Recognizes Control component implementation and installs a
custom activator to ensure that Controls are always created in
the context of the main ui thread. This is necessary since controls
in one thread cannot be added to controls in another.
- Integrates the .Net 2 SynchronizationContext support by allowing
attributes (or configuration) to specify the default SynchronizationContext
of a class with the opportunity to override the context on a per-method
basis. This works seamlessly with the BackgroundWorker component.
Any feedback is welcome and appreciated.
craig[/list][/list] |
|
| Back to top |
|
 |
idavis
Joined: 11 Jul 2007 Posts: 5
|
Posted: Wed Jul 11, 2007 1:23 pm Post subject: |
|
|
I have been trying to get just a simple example to run, but all attempts have failed. I have been trying to get a piece of code that queries a web page for the current time and updates a RichTextBox. It is just a toy app, but it simulates background updating. I have looked over the unit tests many times but I am still missing what exactly is needed to get this code to not throw an InvalidOperationException for cross thread calls. I have removed all Synchronization attributes as nothing I have tried has worked. Do you have any documentation on usages other than the unit tests?
| Code: |
namespace SafelyUpdatingWinForm {
public partial class MainForm : Form {
public MainForm() {
InitializeComponent();
Thread t = new Thread( new ThreadStart( UpdateTime ) );
t.Start();
}
protected virtual void UpdateTime() {
while ( true ) {
string text = GetWebPage( "http://www.time.gov/timezone.cgi?Eastern/d/-5" );
Regex regex = new Regex( @"<b>\d\d:\d\d:\d\d<br>" );
Match match = regex.Match( text );
string time = match.Value.TrimStart( "<b>".ToCharArray() ).TrimEnd( "<br>".ToCharArray() );
richTextBox.Text = time;
Thread.Sleep( 2000 );
}
}
private static string GetWebPage( string page ) { ... }
}
|
TIA,
Ian |
|
| Back to top |
|
 |
cneuwirt
Joined: 24 Jun 2006 Posts: 5
|
Posted: Sat Jul 21, 2007 9:52 pm Post subject: Synchronization issues |
|
|
Assuming you registered the SafelyUpdatingWinForm with the container, your problem might be the fact that the UpdateTime is protected and thus not managed by the synchronization facility. Try making it public. I usually use interfaces for my views to hide the implementation details.
craig |
|
| Back to top |
|
 |
idavis
Joined: 11 Jul 2007 Posts: 5
|
|
| Back to top |
|
 |
idavis
Joined: 11 Jul 2007 Posts: 5
|
Posted: Sun Jul 22, 2007 2:41 pm Post subject: |
|
|
Ok. I couldn't wait until Monday. I have it working now.
In Program.cs:
| Code: |
internal static class Program {
[STAThread]
private static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault( false );
WindsorContainer container = new WindsorContainer();
container.AddFacility( "sync.facility", new SynchronizeFacility() );
container.AddComponent( "mainform.form.class", typeof ( MainForm ) );
Application.Run( container.Resolve( "mainform.form.class" ) as Form );
}
}
|
And then in MainForm.cs:
| Code: |
[Synchronize()]
public partial class MainForm : Form {
private Thread updateLoop;
private bool loop = true;
private const int delay = 2000;
public MainForm() {
InitializeComponent();
updateLoop = new Thread( new ThreadStart( UpdateLoop ) );
updateLoop.Name = "UpdateLoop";
updateLoop.Start();
}
public virtual void UpdateLoop() {
while ( loop ) {
UpdateTime();
Thread.Sleep( delay );
}
}
[Synchronize( typeof ( WindowsFormsSynchronizationContext ) )]
protected virtual void UpdateTime() {
string text = GetWebPage();
Regex regex = new Regex( @"<b>\d\d:\d\d:\d\d<br>" );
Match match = regex.Match( text );
string time = match.Value.TrimStart( "<b>".ToCharArray() ).TrimEnd( "<br>".ToCharArray() );
richTextBox.Text = string.Format( "{0}\t{1}", time, Thread.CurrentThread.Name );
}
private static string GetWebPage() {
string url = "http://www.time.gov/timezone.cgi?Eastern/d/-5";
HttpWebRequest webRequest = WebRequest.Create( url ) as HttpWebRequest;
webRequest.Method = "GET";
using (WebResponse webResponse = webRequest.GetResponse()) {
using (StreamReader sr = new StreamReader( webResponse.GetResponseStream(), Encoding.UTF8 )) {
return sr.ReadToEnd();
}
}
}
protected override void OnClosing( CancelEventArgs e ) {
loop = false;
while ( updateLoop.IsAlive ) {}
base.OnClosing( e );
}
}
|
One mistake I discovered is that I had synchronized the update loop - it is fun watching your app get stuck. Also, the code will work if the attributed method is public, protected, or protected internal. I added the current thread name to the time to show that we are not in the UpdateLoop thread.
Thanks a lot for the facility and your feedback. |
|
| Back to top |
|
 |
cneuwirt
Joined: 24 Jun 2006 Posts: 5
|
Posted: Wed Jul 25, 2007 9:40 pm Post subject: |
|
|
Yup, I need to add the documentation for SynchronizeFacility to the website.
As for your problem, you don't need to do the WindowsSynchronizationContext stuff. You can get the built-in support using without requiring explicit sync attributes. However, you're original code base had 1 small problem. You started the thread in the constructor (instead of the Load event) and the thread started running before your Application.Run completed. As a result, the ISynchornizeInvoke.Invoke was hopelessly blocked since the Windows Message Loop is needed to service the ISynchronizeInvoke.Invoke and that only starts when Application.Run completes. Simple fix, start thread in Load
| Code: |
public partial class MainForm : Form
{
private Thread updateLoop;
private bool loop = true;
private const int delay = 2000;
public MainForm()
{
InitializeComponent();
}
private void UpdateLoop()
{
while (loop)
{
UpdateTime();
Thread.Sleep(delay);
}
}
protected virtual void UpdateTime()
{
string text = GetWebPage();
Regex regex = new Regex(@"<b>\d\d:\d\d:\d\d<br>");
Match match = regex.Match(text);
string time = match.Value.TrimStart("<b>".ToCharArray()).TrimEnd("<br>".ToCharArray());
richTextBox.Text = string.Format("{0}\t{1}", time, Thread.CurrentThread.Name);
}
private static string GetWebPage()
{
string url = "http://www.time.gov/timezone.cgi?Eastern/d/-5";
HttpWebRequest webRequest = WebRequest.Create(url) as HttpWebRequest;
webRequest.Method = "GET";
using (WebResponse webResponse = webRequest.GetResponse())
{
using (StreamReader sr = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8))
{
return sr.ReadToEnd();
}
}
}
protected override void OnClosing(CancelEventArgs e)
{
loop = false;
while (updateLoop.IsAlive)
{
}
base.OnClosing(e);
}
private void MainForm_Load(object sender, System.EventArgs e)
{
updateLoop = new Thread(new ThreadStart(UpdateLoop));
updateLoop.Name = "UpdateLoop";
updateLoop.Start();
}
}
|
|
|
| Back to top |
|
 |
|