Today, as we write application code
we generally are not dealing with immutable state/data, we are applying application logic to state/data so that it transforms from one known state to another known state in a consistent, all or nothing fashion. Today, when performing this in code that does not involve Transaction Managers or Resource Managers, we use Try/Catch exception handling statements to mimick the "Commit" and "Rollback" concepts of transactions. The difficulties with such an approach is that as we scope certain types of work and/or scope more than a few simple actions into a unit of work, the level of complexity in nested try/catch code grows and can easy reach a point where it cannot truely provide the "Commit" and "Rollback" concepts of a Transaction.
For example,
take the Try/Catch below
try
{
// operation(stateX)
// operation(stateY)
// operation(stateZ)
}
catch
{
// cleanup after states X, Y, and/or Z, as something has gone wrong and we need to get to a previously known state
}
The challenges with using try/catch in a Commit/Rollback style are some of the following:
-
If stateX is shared between between more that one execution contexts, another execution context may read an inconsistent state and hence a "dirty" read, as the scoped unit of work may fail
- What if an operation on stateY deletes/alters a resource and how do we "Rollback" in the catch.
- How do we coordinate with resources that are transactional, i.e. if stateX represents a call to a transaction queuing/messaging or database system. How we coodinate between the different resources and unit of works!
- and there is lots of other problems
While all of the above can be eventually handled in code, with careful planning and structure, but what is the essence of what we are trying to achieve in the code and does it form a pattern! It is the "Commit" and "Rollback" concepts of a Transaction and if we factor the patterns, interfaces, contracts and interactions of Transacional Processing into the code we have more flexibility and agility. Please note I am not suggesting that every try/catch has a "Commit" or "Rollback" focus, but we sometimes use try/catch to group actions into a unit of work in which we determine the outcome of a unit of work by interrogation (using if statements) and do not negotiate/coordinate the outcome of the unit of work (as an example). Also, to provide the most flexibility and agility, we need for the actions that are grouped into a unit of work to be able to be aware of the (transactional) context. We also have the problem that try/catch is not context aware, i.e. it only knows a single execution context, and while dealing with a single execution context may suit us some times, we need to be able to handle both concurrency management and recovery control at the same time!
One approach to providing a cleaner implementation, is to factor out the interrogation and make it the negotiation/coordination of the outcome from the application logic and for an intermediary to work as a coordinator between the resources being used and the application logic. The coordinator controls the enlistment of resources within a scoped execution context (as defined by the application code) that we want to be transactional. We then need to define a number of common interfaces and interaction semantics, so that the use/enlistment of one or more resources in the scope/context of a unit of work is clearly defined. This can be achieved by having methods/semantics/behaviors common to all resources so that the different stages of moving/transforming from one known state to another known state as a unit of work or as a part of a unit of work, happens in an all or nothing fashion.
All of this fits quite nicely with the 2 phase commit (and even the one phase commit) protocol as to how the "Commit" or "Rollback" semantics of units of work can be achieved.
Oppps ....... Hmmmm ..... since I thought I was going to be able to do this is 3 to 4 postings, I seem to have underestimated and need one or two more. I think I will leave it here for now with this posting and follow up with a part 4,5,6
In general, when we think of Transactions we tend to think of database transactions. Databases are a great example of how a data server can implement transactions and therefore handle concurrency, provide isolation and provide consistency. The concept of what constitutes a Transaction expands beyond that of a Database Transaction and with this we have more and more areas where the abstraction and contracts that make up a Transaction can help us with the problem of how to handle concurrency management and recovery, as we apply application logic to state and consistently move from one known state to another.
So when we look at a Transaction, what are main parts involved? At a base level it is composed of three parties, i.e. an Application, a resource manager and a transaction manager. So how they are involved in a transaction and what is their role in a transaction?
The Application is pretty self explanatory, it’s your code. Its role in a transaction is to define the scope of a transaction, perform read/write operations on a resource manger and specify whether it views the state as being consistent. A resource manager is a component that manages a stable data storage system (i.e. it always shows a consistent view of data/state), and participates in the two phase commit and recovery protocols with the transaction manager. A transaction manager tracks which resource managers participate in the transaction, provides an interface to the application – start, commit, rollback and also, coordinates the transaction outcome between the resource managers. The transaction manager has the responsibility of properly handling error conditions, on behalf of an application, recover from them and ensure that the resource managers are always in a consistent state.
With today’s technologies the Resource Managers that typically get used are those of databases systems and queue managers. But is there other times when you need to be transforming/moving from one state to another? There certainly are!
The basic premise of Transactions is the coordination/negotation of an outcome that groups actions into a unit of work that transforms from one known state to another. As the transformation of state, the coordination/negotation of an outcome, or the grouping of action into a unit of work are something we do very often in application logic, is this not a Transaction.
So when do we get involved in the transformation of state, the coordination/negotation of an outcome, or the grouping of action(s) into a unit of work? What are we trying to achieve, how do we generally do it today and is there a better way to do it?
I've kicked off Part 3, to share my views on this and hopefully provide some insight!
When thinking about Transactions, I think it is fair to say that Transactions are one of the most powerful and frequently used paradigms in Enterprise Computing today. With Transactions, we have a collection of patterns that deal with how concurrency management and recovery control can be handled in possible shared/concurrent environments/contexts and it is this that ensures data/state correctness. It is the key concepts and abstractions of Transactions maintaining data/state consistency in possible concurrent state/data access environments (where failures may occur) that have allowed us as developers and architects to be largely released from having to deal with the tricky aspect of maintaining the correctness of state in concurrent environments in an enterprise application.
Two of the very interesting aspects of Transactions is how trusted they have become and how we perceive them. Concurrency Management and recovery control are some of the more tricky aspects of Enterprise Computing, but yet when leveraging the Transactional paradigm and executing units of work in a concurrent environment in the context of a transaction, we are not overly concerned about isolation or failure as much of the work is done for us by the Transaction Paradigm. This is a testament to the level of trust and adoption that Transactions have achieved as an abstraction and how it has evolved into a framework of runtime services that takes most of the responsibility for concurrency management and recovery control from us as developers and architects. The paradigm of Transactions can be seen as a set of patterns, Interfaces and Contracts that can be leveraged to simplify the transformation from one known state to another and without these Interfaces and Contracts; a lot of code, time and effort has to be spend in making sure that data/state transformations happen in a consistent and all or nothing fashion.
It is these key abstractions in the form of runtime services or frameworks that have provided great productive gains for us as developers and architects, as it allows us to focus on business logic and allows for the underlying runtime services to deal with concurrency management and recovery control. Therefore as we implement application logic, by applying units of work to state, we want the transformation from one state to another to be applied in a consistent (all or nothing) fashion, as well as wanting to be isolated from other parallel activities. As I mentioned in my
How should we think of Transactions ?
blog posting, I like to think of the Transaction paradigm as a collection of patterns, interfaces and contracts that can be leveraged to tackle the issue of data/state correctness as we group actions into unit of works to transform from one known state to another. Some of this collection of patterns, interfaces and contracts are short running transactions that in the main adhere to the ACID properties but there is a rich portfolio of transactional types that we do not focus on that much and/or maybe perceive as not meeting the ACID properties (see
How should we think of Transactions ?
and
Volatile Transactions & In-Memory Transactions - do they have ACID, ACId or ACI properties?
)
Today, the most common example of how the Transaction paradigm can provide concurrency management and recovery control is Database Management Systems (DBMS). Databases (aka DBMS) are themselves another key and often used architectural piece of Enterprise computing. Database Management Systems (DBMS) provide a paramount role in how an Enterprise may store, retrieve and share state/data. But we sometimes draw an association between Databases and Transactions that is so close that they are often perceived as being interchangeable, i.e. Transactions are DB Transactions and nothing else. The close associations between databases systems and transactional processing have been fundamental in bringing the concept of Transactional Processing to the fore, but in my honest opinion I think that the close association may have also brought a narrowing of perception of what are Transactions and Transactional Processing.
I have put together a series of blog postings to share my current views/thoughts! After talking to a few folks about Transactions, it is very interesting to hear the different views and I'm not sure if there is a one size fits all for what is a transaction, but this is the thing about a paradigm and a snappy acronym, it can have a life of its own
Maybe we interchange the paradigm, acronym’s and implementations into one fixed view and its this fixed view that maybe causing the issue!
In my last blog posting,
How should we think of Transactions ?
I mentioned Volatile transactions and how I currently think they can be perceived in different ways! Do they have ACID, ACId or ACI properties?
In case you do not know what Volatile Transactions (aka In-Memory Transactions) are, they are transactions that do not persist to a durable store such as a hard disk (ouch! we have multiple definitions of durable to content with
). They are transactions that are done completely in memory and do not incur the I/O cost of writing to a hard disk a Transaction log or the actual known data/state.
What is interesting about Volatile transactions is that they are sometimes perceived as not being Transactional, because they are viewed as not meeting the ACID properties, especially the "D" property. I suspect the main reason for this view is because there are no Transaction logs and/or no data stored to hard disk.
So when you look at a typical definition of the ACID properties (like what I have listed below), some of the reasons why this view maybe held become apparent:
(NB. this is an amalgamation/synopsis of many definitions I have seen)
A
- Atomicity - Transactions are atomic (all or nothing)
C
- Consistency - The transaction is a correct transformation of state. Consistency constraints that are defined on the underlying data servers are preserved by a transaction.
I
- Isolation - Each transaction should appear to execute independently of other transactions that may be executing concurrently in the same environment.
D
- Durability - Once a transaction commits, it’s updates survive even, if the system goes down
ok, so a Volatile Transaction meets the ACI of the ACID properties, but not the D as defined above. But is this ("Once a transaction commits, it’s updates survive even, if the system goes down") a true reflection of what Durability means? Many similar variants of this definition gets used on a frequent based and they bring specifics to the definiton that are not about the failure of meeting the ACI properties or making the "Commited" value available/public but focus on the failure of media or a system. In my opinion the failure of media or a system is not an aspect of the paradigm or indeed part of the ACID properties, but an aspect of how the paradigm can be implemented and/or a requirement of the longevity of the state being persisted/determined. It is perhaps the fact that the best known implementation of the data server feature of the transactional paradigm is a database that has led us to shape the ACID properties to the specifics of databases. But perhaps the Transactional paradigm has more to offer than the correctness of data/state transformations on a database. The transaction paradigm has a core focus on simpilifing how we can achieve the correctness of data/state transformations where we may have to deal with concurrency management and recovery control and this can be applied to any transformation of state.
Perhaps a different way to look at the durability of ACID is where the definition of ACID as an acronym came from and how some of the leading experts in the industry define them. As far as I know the ACID acronym was first coined by Haerder and Reuter (1983). The definition I like to use is that by Gray and Reuter (1993). It is the following:
A
- Atomicity - A transaction's changes to state are atomic: either all happens or none happen. These changes include database changes, messages, and all actions on transducers.
C
- Consistency - A transaction is a correct transformation of the state. The actions taken as a group do not violate any of the integrity constraints associated with the state. This requires that the transaction be a correct program.
I
- Isolation - Even though transactions execute concurrently, it appears to each transaction, T, that others executed either before T or after T, but not both
D
- Durability - Once a transaction completes successfully (commits), its changes to the state survive failures.
When you read this definition of Durability (and the ACID properties overall), it in my option explains the feature richness of transactions and how it can be applied to the persistence of the transformation of state and also how it applies to the other properties such as A, C and I. The state survives failures by the data server ensuring that when the transaction commits a new durable state is persisted/stable/available/public and if something goes wrong (such as not be able to ensure Consistency) that the data server returns to the previous known state. In essence, this is a contract that guarantees the persistence and stability of the state and that the outcome of a transaction is known/public and usable in subsequent transactions, as a known/correct state. A good definition I have seen is that once a transaction is committed, it cannot be abrogated.
With Durability being about the persistence, stability and making public of “commited”/known state as we transform from one known/correct state to another, it allows us to leverage the transaction paradigm in many scenarios. For example when using Volatile Transactions, we are leveraging data servers that store data on Volatile media, such as RAM and providing the transaction paradigm in-memory. With the common misunderstanding that Transactions are exclusively for databases, the question arises about the “D” property.
For me the Durability aspect of a transaction is present in a Volatile transaction. The question is whether its “D” or “d”. Since most transactions are based on the X/Open Transaction processing model, the requirement for log managers or lock managers in resource managers (data servers) are not central or core to implementing the transactional paradigm at a base level. In relation to the usage of Volatile media for Transactions? in my opinion the requirement of the media used by the data server is that it is able to present a stable/persistent/public state once "Commit" has occured.
So I believe that Volatile Transactions meet all of the ACID properties and that its ACID with a "D"
phew! long post.
I guess the crux of the issue is the 2 uses of "durable" when talking about Transactions. One is the "D" in the ACID properties and the other is the property of the data/state/media being used (and how it is stored). The "D" in the ACID properties is more about making a "commited" value available and the other is about an implementation requirement of how long (and/or resilient) a known/committed state must be.
What is particularily interesting about HTTP.SYS is that it has an API and it can be used to write/roll your own HTTP endpoint. Nice!!
Currently there is only
a C API
, but with Whidbey you have access to a managed API to HTTP.SYS.
Here is a sample (via
Don's blog
) of the
HTTPListener class
that comes in Whidbey
using System;
using System.Net;
using System.IO;
class Program
{
static void Main(string[] args)
{
HttpListener http = new HttpListener();
string uri = string.Format("
http://localhost/{0}/
", args[0]);
http.Prefixes.Add(uri);
http.Start();
while (true)
{
HttpListenerContext ctx = http.GetContext();
ctx.Response.ContentType = "text/html";
TextWriter writer = new StreamWriter(ctx.Response.OutputStream);
writer.WriteLine("
It's now {0}{1}",
DateTime.Now,
ctx.Request.Url);
writer.Flush();
writer.Close();
}
}
}
Details about for the C API can be found at
http://msdn.microsoft.com/library/en-us/http/http/http_api_overview.asp?frame=true
and details for the Whidbey API (aka the
HTTPListener class
) can be found at at
http://msdn2.microsoft.com/library/34xswsd2.aspx
.
Also,
Aaron Skonnard
has just published a very good article on
Running ASPX without IIS
. Its published on the
lastest version of the MSDN magazine
and covers HttpListener, as well as how to host ASPX in your own custom process.
The most significant changes within the new architecture for IIS 6.0 are:
- a new process model called WPIM ( Worker Process Isolation Mode)
- Application Pooling and Recycling
- HTTP.SYS - it is a HTTP listener that is a kernel mode driver and is a common endpoint for HTTP traffic on the platform
- WWW Service Administration and Monitoring (W3ADM). It is part of the WWW Service.
- the IIS Metabase is in XML format
Below you will see how a HTTP request is processed by HTTP.SYS and passed onto the IIS architecture components.
When an HTTP request arrives at the kernel-mode HTTP Listener/Endpoint, it checks the validity of the request. If the request is invalid, the appropriate HTTP error is immediately returned to the requester. If the request is valid, HTTP.sys checks to see if it can handle the request from its cache. If the response is in the cache, HTTP.sys sends the response immediately. Otherwise, HTTP.sys puts the request in a separate request queue for each worker process (application).If the necessary worker process is not listening on the request queue, then HTTP.sys signals the W3ADM to start and configure a worker process based on the configuration information stored in XML format in the metabase.
If a worker process or a group of worker processes (called a Web garden) is already started and connected to the request queue, the worker process pulls the request from the queue, processes it through any ISAPI filter or extension and Web application code, and returns the response to the HTTP.sys and the requester.
The W3ADM also monitors the health of a worker process and if, for example, the process does not respond or has exceeded a threshold (e.g., the number of hours running or number of requests handled), WAS coordinates with the HTTP Listener to hold requests in the queue while W3ADM stops the worker process and restarts it.
In response to my
entry on refactoring in VB 2005
, some have asked why VB couldn't take advantage of C#'s work on refactoring the same way that C# took advantage of VB's work on Edit and Continue. I anticipated this would come up, and the answer is that some features are
subject to drafting
at the current time and some aren't.
Edit and Continue is a feature that spans the compiler, the debugger and the CLR. As such, work done in the latter two areas can be applied across all the languages that want to take advantage of Edit and Continue. Refactoring, on the other hand, is a feature that is currently implemented entirely within the compiler. Thus, there's no opportunity for taking advantage of the work that other teams have done on the feature.
Now, one could certainly imagine a world where it might be
possible
to share the work on refactoring across the languages, but we're just not there yet. Refactoring requires an intimate understanding of the structure and meaning of the code that's being modified, which is why it's currently implemented as a part of each language's IDE component. If there was an abstraction layer, however, that could abstract away the different syntax and semantics of the languages to such a degree that one could manipulate C# and VB code in a language-independent way that preserved semantics, then common refactorings would be possible. At the moment, though, neither VB nor C# is architected in this way.
Given some of my experiences with the CodeDOM and WinForms designer trying to do a much simpler form of this, I'm not entirely sanguine that such an abstraction layer is possible. Perhaps some of the third-party refactoring tools will prove me wrong on this. It's something that we talk about and will think about as we move past 2005, but for now each language is on it's own as far as refactorings...
The problem with making
confident predictions about the future
is that time has a way of making a liar out of you despite your best intentions. As Steven
posted Friday
, VB will not have any refactoring features beyond "rename symbol" in VB 2005. This was an extremely painful cut and one that we tried to move heaven and earth to avoid, but in the end we didn't feel we could get the features into the product in a high-quality way without causing serious risk to our shipping goals. Balacing the cries for refactoring against the cries for us to ship as soon as possible was difficult, and we really regret the fact that it ain't going to happen this time (for the most part... it's not like rename symbol is chopped liver or anything!).
On the other hand, it does mean that we get a while longer to have lots of
colorful, humorous arguments
about whether we should have a "Refactoring" menu in VB or not. Oh, joy...
Some people were curious what I was getting at with my
quick question yesterday
, so now that I've got a little more time to explain, let me...
The problem that we're looking at right now has to do with some subtle interactions between several new debugging features in VB 2005. One of the features is what we call Design Time Expression Evalutation (abbreviated to "DTEE"), which is just a fancy way of saying "you can evaluate expressions and call functions from the Immediate window even when your application isn't running." This was a nice feature from past versions of VB that we lost in VB 2002 and we're happy to bring back in VB 2005.
The way DTEE works is that it actually starts up a copy of your application in the background when you try to evaluate something at design time. This background copy of your application is called the
vshost process
, and what the debugger does is set a breakpoint at the very beginning of the program to prevent the vshost application from actually running and doing things like putting up UI, etc. The vshost process then sits there in a debug break state, waiting for the user to ask to debug the application or do DTEE.
Another feature that I think we introduced in VB 2003 but are extending in VB 2005 is something called "Just My Code." This is a debugger feature that filters out all the non-user code from places like the callstack window and prevents the application from breaking in non-user code. This means that if you call a .NET Framework API that throws an exception, you're going to break in your code that called the API rather than way down in the Framework (since you likely don't have any source code for the Framework). You can always turn off JMC in the options dialog if you want to see non-user code, BTW.
Now, let's think about a new WinForms project for a moment. A new WinForms project contains no actual user code, because all the code in the hidden partial form class is marked as non-user code. So... can you see the problem? Right. You can't do DTEE on a new WinForms project because the vshost process tries to put the breakpoint at the beginning of the program, but since the beginning of the program is not user code, JMC prevents the breakpoint from getting hit.
This isn't a huge, huge deal - there are a couple of ways we can attack this problem. One proposed way would be to make changes to the debugger to recognize this kind of situation and handle it in a different way. Another proposed way was to change the way that we handle Sub New in the hidden partial class, perhaps making it compiler generated (and thus allowing us to fiddle with the way the debugger sees it). This would mean automatically generating the call to InitializeComponent as well, but what happens if the user specifies their own Sub New? Should we still automatically generate the call to InitializeComponent? If so, should it be before or after the user code in Sub New?
So the purpose of my request for feedback was to get a general feel for how we might have to proceed if we went down that path. The most important thing, of course, is to
First, do no harm
(to
mis-quote
the Hippocratic Oath). In solving one problem, we don't want to create
more
problems for people.
I'll let you know what we end up doing in the comments here... Thanks for all the quick feedback!
A quick request for feedback... How often do you:
- Use Sub New in a form instead of handling the form's Load event?
-
When you do use Sub New, are there things that you have to do
before
the call to InitializeComponent, or do things generally go
after
the call to InitializeComponent?
We're thinking about how to solve some issues that we've uncovered in the beta and this feedback might help in deciding how to deal with them. Thanks!
I was chatting with some of the people on our performance team (i.e. the people who are supposed to make sure VB is as speedy as possible), and they were talking about some plans they were considering to help gather public feedback on problem areas in the product. "What about the MSDN Product Feedback Center?" I asked. "We hadn't thought of that," they replied, "perhaps you should blog about it!" So consider it done. I am now officially encouraging everyone to report any major performance problems you're having with VB using the
MSDN Product Feedback Center
.
To ensure that your bug gets the amount of attention due it, here are several suggestions:
- Put the word "Performance:" in the title of the bug.
- Include the general hardware specifications (CPU speed, memory, etc.) that you're testing on.
- Include any changes to the environment that you've made (i.e. what tool windows are showing, which profile you are using, etc.).
And, most importantly:
- Include specific steps that will allow us to easily reproduce the problem.
The last point is important to ensure that we actually can do something in regards to your bug report. Saying "the product is slow," isn't going to help us. Saying "when I show the Autos window and then hit the Step Through toolbar button on this code, it takes several seconds." is more like it. Remember, the more specific you are, the higher chance we'll be able to fix the problem.
I'm also talking to Robert Green to see if we can't get a specific "Performance" Problem Type added to the Feedback Center to help categorization. The wheels grind slowly, though, so it may be a while on that one.
Warning: Seriously geeky stuff ahead. May not be suitable for children or anyone who doesn't love the minutia of JIT optimizations and the CLR memory model.
A reader, Russ, asks:
Given the importance of volatile in light of Greg's comments here:
http://blogs.msdn.com/grantri/archive/2004/09/07/226355.aspx
. why doesn't vb.net support a similar construct? or are we not allowed to write high-performance multithreaded code using vb? :p
The construct that Russ is referring to is
volatile fields
. C# allows you to declare field with the “volatile” modifier, which results in all accesses to the field being prepended in the IL with the opcode “volatile.” I'd like to pretend that I could adequately explain to you what volatile reads and writes actually do, but there is a high probability that I would get it wrong because the CLR memory model is not my thing. It is, however, Vance's thing and you can
check out a message he wrote
that talks in more detail about the subject.
Although VB doesn't support the volatile modifier, you're not up a creek without a paddle. In fact, as I said above, marking a field as volatile in C# doesn't do anything to the field itself - it just makes sure that the compiler prefixes all reads and writes with the volatile modifier. Volatility is really a property of reading and writing from a field, not a property of the field itself. In fact, there may be situations where you only care at certain points whether you do a volatile read or write, in which case the volitile field modifier is overkill (because it makes
all
reads and writes volatile). A slightly more ideal situation would be able to mark just a particular read or write as being volatile.
The CLR provides a way to do this, sort of. The type System.Threading.Thread provides VolatileRead and VolatileWrite methods that allow you to do volatile reads and writes from fields without having to do any declarative work at all. So, everything is right with the world, correct? Unfortunately, no. If you look at the definition of VolatileRead and VolatileWrite, they use reference parameters (ByRef) to refer to the field that you're reading or writing from. This is necessary because if they were regular value parameters (ByVal) the VolatileWrite wouldn't work at all and VolatileRead wouldn't give you the desired behavior. (That's because value parameters are copied onto the stack. That copy wouldn't be a volatile read. When you pass the address of the value, then the VolatileRead method can do a volatile read of the value at that address.)
The problem is that when you pass a variable to a reference parameter, the type of the variable must exactly match the type of the reference parameter. If it doesn't, then you have to create a temporary variable of the right type, copy the value into the temporary, call the method and then copy the value out of the temporary. This isn't an issue for primitive types because there are overloads of VolatileRead and VolatileWrite for all the primitive types. But what about some random reference type like an array (which occurs in Grant's example)? The closest overload is a ByRef parameter of Object, but that's still not going to be an exact match. That means you fall into the copy-in/copy-out scheme I talked about a moment ago, which screws everything up because the reads and writes to the temporary variable are not volatile! Arrrrg!
So, are VB users just screwed? Fortunately, no. There's another method on System.Threading.Thread that can help out here: MemoryBarrier. When you call MemoryBarrier before a write or after a read, you effectively make the read or write a volitile read or write. (In fact, in Whidbey this is how VolatileRead and VolatileWrite are implemented.) You could even write a generic VolatileRead and VolatileWrite to do this for you in VB 2005:
Function VolatileRead(Of T)(ByRef Address As T) As T
VolatileRead = Address
Threading.Thread.MemoryBarrier()
End Function
Sub VolatileWrite(Of T)(ByRef Address As T, ByVal Value As T)
Threading.Thread.MemoryBarrier()
Address = Value
End Sub
So, yes, VB users are allowed to write high-performance multithreaded code... :)
One question I see a lot has to do with the mysterious “vshost” application that appears when you build your application. If you're writing foo.exe, you'll notice that there's a foo.vshost.exe that gets produced and is running while you're working in the editor. What is it?
Dave Templin
, VB lead for deployment,
tells you all about it
...
Jesse Liberty has written a
very nice article
on My for O'Reilly's
OnDotnet.com
. He starts with a theme we've seen a lot of over the years:
For a couple of years now, I've been touting the Microsoft-endorsed sentiment that it really doesn't matter if you program in C# or in VB.NET, since both are just syntactic sugar layered on top of MSIL (Microsoft Intermediate Language, the true language of .NET).
But then adds:
That appears to be changing a bit with Whidbey.
He then talks about some of the features you'll get with My. He concludes with:
The
My
object has made creating this application almost absurdly easy. [...] VB 2 has taken a dramatic lead in Rapid Application Development with the
My
object.
Go check it out!
In my
previous entry on events
(written well before even VS 2002 had shipped), I made the comment:
VB does not have a syntax for defining events that allows you to specify the field, the add method or the remove method. Those are always implicitly generated.
Now, most of the time this doesn't really matter. Most of the time, the code you write in the add and remove method is going to be the same boilerplate code over and over and over again, so you're going to want to just let the compiler do its thing and not worry about it too much. However, there are some situations in which you might want to take over managing an event's delegate. The most common case that I know of is the situation in which you have an object that raises a lot of events. For example, a Form can raise something like 85 different events. If you accept the default compiler behavior, this means that the compiler will generate a field for each and every event to store the event handlers for that event. Which means in the case of Form, that it would generate something like 85 fields, even though in most cases programmers only ever handle about 4-5 events on a Form!
One alternative to wasting all that space is to use a hashtable to store delegates for just the events that someone is handling. To do this, though, you need to be able to control what happens when someone hooks up to or unhooks from an event. So, in VB 2005, we're introducing something we call custom events that look something like this:
Class C1
Public Custom Event MyEvent As EventHandler
AddHandler(ByVal d As EventHandler)
...
End AddHandler
RemoveHandler(ByVal d As EventHandler)
...
End RemoveHandler
RaiseEvent(ByVal o As Sender, ByVal e As EventArgs)
...
End RaiseEvent
End Event
End Class
Custom events are declared with the Custom modified on the event declaration and have to explicitly state their delegate type. Custom events have three parts: an AddHandler method that is called when someone is hooking up to the event, a RemoveHandler method that is called when someone unhooks from the event and a RaiseEvent method that is called when the class does a RaiseEvent on the event. The AddHandler and RemoveHandler methods take a delegate of the type of the event. The RaiseEvent method takes the same parameters as the event delegate does. So, to store all event delegates in one hashtable, you could do the following:
Class C1
Private EventDelegates As New Dictionary(Of String, EventHandler)
Private Sub AddNewHandler(ByVal eventName As String, ByVal handler As EventHandler)
If EventDelegates.ContainsKey(eventName) Then
EventDelegates(eventName) = CType([Delegate].Combine(EventDelegates(eventName), handler), EventHandler)
Else
EventDelegates(eventName) = handler
End If
End Sub
Private Sub RemoveExistingHandler(ByVal eventName As String, ByVal handler As EventHandler)
If EventDelegates.ContainsKey(eventName) Then
EventDelegates(eventName) = CType([Delegate].Remove(EventDelegates(eventName), handler), EventHandler)
End If
End Sub
Private Sub RaiseOneEvent(ByVal eventName As String, ByVal sender As Object, ByVal e As EventArgs)
If EventDelegates.ContainsKey(eventName) Then
Dim p As EventHandler = EventDelegates(eventName)
If p IsNot Nothing Then
p.Invoke(sender, e)
End If
End If
End Sub
Public Custom Event MyEvent As EventHandler
AddHandler(ByVal d As EventHandler)
AddNewHandler("MyEvent", d)
End AddHandler
RemoveHandler(ByVal d As EventHandler)
RemoveExistingHandler("MyEvent", d)
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
RaiseOneEvent("MyEvent", sender, e)
End RaiseEvent
End Event
End Class
One thing to notice that's different from C# is that we make you specify a RaiseEvent method. This is to enable the RaiseEvent statement, which C# doesn't have, to work properly. Otherwise, it works pretty much the same way C#'s event declaration does.
|